diff --git a/app/app.go b/app/app.go index 97be31f7fa..2055653c43 100644 --- a/app/app.go +++ b/app/app.go @@ -172,9 +172,6 @@ import ( oraclekeeper "github.com/provenance-io/provenance/x/oracle/keeper" oraclemodule "github.com/provenance-io/provenance/x/oracle/module" oracletypes "github.com/provenance-io/provenance/x/oracle/types" - rewardkeeper "github.com/provenance-io/provenance/x/reward/keeper" - rewardmodule "github.com/provenance-io/provenance/x/reward/module" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" triggerkeeper "github.com/provenance-io/provenance/x/trigger/keeper" triggermodule "github.com/provenance-io/provenance/x/trigger/module" triggertypes "github.com/provenance-io/provenance/x/trigger/types" @@ -232,7 +229,6 @@ var ( metadata.AppModuleBasic{}, wasm.AppModuleBasic{}, msgfeesmodule.AppModuleBasic{}, - rewardmodule.AppModuleBasic{}, triggermodule.AppModuleBasic{}, oraclemodule.AppModuleBasic{}, holdmodule.AppModuleBasic{}, @@ -255,7 +251,6 @@ var ( attributetypes.ModuleName: nil, markertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, wasmtypes.ModuleName: {authtypes.Burner}, - rewardtypes.ModuleName: nil, triggertypes.ModuleName: nil, oracletypes.ModuleName: nil, } @@ -311,7 +306,6 @@ type App struct { EvidenceKeeper evidencekeeper.Keeper FeeGrantKeeper feegrantkeeper.Keeper MsgFeesKeeper msgfeeskeeper.Keeper - RewardKeeper rewardkeeper.Keeper // QuarantineKeeper quarantinekeeper.Keeper // TODO[1760]: quarantine // SanctionKeeper sanctionkeeper.Keeper // TODO[1760]: sanction TriggerKeeper triggerkeeper.Keeper @@ -410,7 +404,6 @@ func New( nametypes.StoreKey, msgfeestypes.StoreKey, wasmtypes.StoreKey, - rewardtypes.StoreKey, // quarantine.StoreKey, // TODO[1760]: quarantine // sanction.StoreKey, // TODO[1760]: sanction triggertypes.StoreKey, @@ -517,8 +510,6 @@ func New( stakingtypes.NewMultiStakingHooks(restrictHooks, app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), ) - app.RewardKeeper = rewardkeeper.NewKeeper(appCodec, keys[rewardtypes.StoreKey], app.StakingKeeper, &app.GovKeeper, app.BankKeeper, app.AccountKeeper) - app.AuthzKeeper = authzkeeper.NewKeeper(runtime.NewKVStoreService(keys[authzkeeper.StoreKey]), appCodec, app.BaseApp.MsgServiceRouter(), app.AccountKeeper) app.GroupKeeper = groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.BaseApp.MsgServiceRouter(), app.AccountKeeper, group.DefaultConfig()) @@ -583,7 +574,6 @@ func New( markerReqAttrBypassAddrs := []sdk.AccAddress{ authtypes.NewModuleAddress(authtypes.FeeCollectorName), // Allow collecting fees in restricted coins. - authtypes.NewModuleAddress(rewardtypes.ModuleName), // Allow rewards to hold onto restricted coins. // authtypes.NewModuleAddress(quarantine.ModuleName), // Allow quarantine to hold onto restricted coins. // TODO[1760]: quarantine authtypes.NewModuleAddress(govtypes.ModuleName), // Allow restricted coins in deposits. authtypes.NewModuleAddress(distrtypes.ModuleName), // Allow fee denoms to be restricted coins. @@ -802,7 +792,6 @@ func New( attribute.NewAppModule(appCodec, app.AttributeKeeper, app.AccountKeeper, app.BankKeeper, app.NameKeeper), msgfeesmodule.NewAppModule(appCodec, app.MsgFeesKeeper, app.interfaceRegistry), wasm.NewAppModule(appCodec, app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, nil, app.GetSubspace(wasmtypes.ModuleName)), // TODO[1760]: Need to pass message router instead of nil - rewardmodule.NewAppModule(appCodec, app.RewardKeeper, app.AccountKeeper, app.BankKeeper), triggermodule.NewAppModule(appCodec, app.TriggerKeeper, app.AccountKeeper, app.BankKeeper), oracleModule, holdmodule.NewAppModule(appCodec, app.HoldKeeper), @@ -857,7 +846,6 @@ func New( markertypes.ModuleName, icatypes.ModuleName, attributetypes.ModuleName, - rewardtypes.ModuleName, triggertypes.ModuleName, // no-ops @@ -894,7 +882,6 @@ func New( authtypes.ModuleName, icatypes.ModuleName, group.ModuleName, - rewardtypes.ModuleName, triggertypes.ModuleName, // no-ops @@ -968,7 +955,6 @@ func New( ibchookstypes.ModuleName, // wasm after ibc transfer wasmtypes.ModuleName, - rewardtypes.ModuleName, triggertypes.ModuleName, oracletypes.ModuleName, @@ -1015,7 +1001,6 @@ func New( msgfeestypes.ModuleName, metadatatypes.ModuleName, nametypes.ModuleName, - rewardtypes.ModuleName, triggertypes.ModuleName, oracletypes.ModuleName, @@ -1049,7 +1034,6 @@ func New( name.NewAppModule(appCodec, app.NameKeeper, app.AccountKeeper, app.BankKeeper), attribute.NewAppModule(appCodec, app.AttributeKeeper, app.AccountKeeper, app.BankKeeper, app.NameKeeper), msgfeesmodule.NewAppModule(appCodec, app.MsgFeesKeeper, app.interfaceRegistry), - rewardmodule.NewAppModule(appCodec, app.RewardKeeper, app.AccountKeeper, app.BankKeeper), triggermodule.NewAppModule(appCodec, app.TriggerKeeper, app.AccountKeeper, app.BankKeeper), oraclemodule.NewAppModule(appCodec, app.OracleKeeper, app.AccountKeeper, app.BankKeeper, app.IBCKeeper.ChannelKeeper), holdmodule.NewAppModule(appCodec, app.HoldKeeper), @@ -1423,7 +1407,6 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(attributetypes.ModuleName) // TODO[1760]: params: Migrate attribute params. paramsKeeper.Subspace(msgfeestypes.ModuleName) // TODO[1760]: params: Migrate msgFees params. paramsKeeper.Subspace(wasmtypes.ModuleName) - paramsKeeper.Subspace(rewardtypes.ModuleName) // TODO[1760]: params: Migrate reward params. paramsKeeper.Subspace(triggertypes.ModuleName) // TODO[1760]: params: Migrate trigger params. // register the key tables for legacy param subspaces diff --git a/app/test_helpers.go b/app/test_helpers.go index 84b40f8d12..658ce0bea3 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -43,7 +43,6 @@ import ( "github.com/provenance-io/provenance/app/params" "github.com/provenance-io/provenance/internal" "github.com/provenance-io/provenance/internal/pioconfig" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" ) // DefaultConsensusParams defines the default Tendermint consensus params used in @@ -516,64 +515,3 @@ func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { } return &ed25519.PubKey{Key: pkBytes} } - -// SetupWithGenesisRewardsProgram initializes a new SimApp with the provided -// rewards programs, genesis accounts, validators, and balances. -func SetupWithGenesisRewardsProgram(t *testing.T, nextRewardProgramID uint64, genesisRewards []rewardtypes.RewardProgram, genAccs []authtypes.GenesisAccount, valSet *cmttypes.ValidatorSet, balances ...banktypes.Balance) *App { - t.Helper() - - // Make sure there's a validator set with at least one validator in it. - if valSet == nil || len(valSet.Validators) == 0 { - privVal := mock.NewPV() - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) - validator := cmttypes.NewValidator(pubKey, 1) - if valSet == nil { - valSet = cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}) - } else { - require.NoError(t, valSet.UpdateWithChangeSet([]*cmttypes.Validator{validator})) - } - } - - app, genesisState := setup(t, true, 0, "") - genesisState = genesisStateWithValSet(t, app, genesisState, valSet, genAccs, balances...) - genesisState = genesisStateWithRewards(t, app, genesisState, nextRewardProgramID, genesisRewards) - - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - require.NoError(t, err, "marshaling genesis state to json") - - _, err = app.InitChain( - &abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - require.NoError(t, err, "InitChain") - - _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: app.LastBlockHeight() + 1, - Hash: app.LastCommitID().Hash, - NextValidatorsHash: valSet.Hash(), - Time: time.Now().UTC(), - }) - require.NoError(t, err, "FinalizeBlock") - - return app -} - -func genesisStateWithRewards(t *testing.T, - app *App, genesisState GenesisState, - nextRewardProgramID uint64, genesisRewards []rewardtypes.RewardProgram, -) GenesisState { - rewardGenesisState := rewardtypes.NewGenesisState( - nextRewardProgramID, - genesisRewards, - []rewardtypes.ClaimPeriodRewardDistribution{}, - []rewardtypes.RewardAccountState{}, - ) - var err error - genesisState[rewardtypes.ModuleName], err = app.AppCodec().MarshalJSON(rewardGenesisState) - require.NoError(t, err, "marshaling reward genesis state JSON") - return genesisState -} diff --git a/internal/handlers/msg_service_router_test.go b/internal/handlers/msg_service_router_test.go index f3a52e5cb7..c20c7f8bc0 100644 --- a/internal/handlers/msg_service_router_test.go +++ b/internal/handlers/msg_service_router_test.go @@ -14,7 +14,6 @@ import ( cmttypes "github.com/cometbft/cometbft/types" "cosmossdk.io/log" - sdkmath "cosmossdk.io/math" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/baseapp" @@ -33,7 +32,6 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" piosimapp "github.com/provenance-io/provenance/app" "github.com/provenance-io/provenance/internal/antewrapper" @@ -41,7 +39,6 @@ import ( "github.com/provenance-io/provenance/internal/pioconfig" "github.com/provenance-io/provenance/x/msgfees/types" msgfeestypes "github.com/provenance-io/provenance/x/msgfees/types" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" ) func stopIfFailed(t *testing.T) { @@ -987,2325 +984,6 @@ func TestMsgServiceAssessMsgFeeNoRecipient(tt *testing.T) { }) } -func TestRewardsProgramStartError(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin("hotdog", 1000), sdk.NewInt64Coin("atom", 1000), - sdk.NewInt64Coin(NHash, 1_190_500_000), - ) - app := piosimapp.SetupWithGenesisAccounts(t, "", - []authtypes.GenesisAccount{acct1}, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - blockTime := ctx.BlockTime() - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(NHash, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - rewardProgram := *rewardtypes.NewMsgCreateRewardProgramRequest( - "title", - "description", - acct1.Address, - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - blockTime, - 0, - 3, - 3, - uint64(blockTime.Day()), - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - - txBytes, err := SignTxAndGetBytes(ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - &rewardProgram, - ) - require.NoError(t, err, "SignTxAndGetBytes") - blockRes, err := app.FinalizeBlock( - &abci.RequestFinalizeBlock{ - Height: ctx.BlockHeight() + 1, - Txs: [][]byte{txBytes}, - }, - ) - assert.Equal(t, uint32(0x1), blockRes.TxResults[0].Code, "claims period error") - assert.NoError(t, err, "FinalizeBlock") -} - -func TestRewardsProgramStart(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - pioconfig.SetProvenanceConfig("nhash", 0) - priv, _, addr := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin("hotdog", 1000), - sdk.NewInt64Coin("atom", 1000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000), - ) - app := piosimapp.SetupWithGenesisAccounts(t, "", - []authtypes.GenesisAccount{acct1}, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContextLegacy(false, cmtproto.Header{Time: time.Now()}) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - rewardProgram := *rewardtypes.NewMsgCreateRewardProgramRequest( - "title", - "description", - acct1.Address, - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time.Now().Add(time.Duration(1)*time.Second), - 9, - 3, - 3, - 10, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - - txReward, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - &rewardProgram, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - blockRes, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: ctx.BlockHeight() + 1, - Time: time.Now().UTC(), - Txs: [][]byte{txReward}, - }, - ) - assert.NoError(t, err, "FinalizeBlock expected no error") - - expEvents := []abci.Event{ - NewEvent(rewardtypes.EventTypeRewardProgramCreated, - NewAttribute(rewardtypes.AttributeKeyRewardProgramID, "1")), - NewEvent(sdk.EventTypeMessage, - NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&rewardtypes.MsgCreateRewardProgramRequest{}))), - } - assertEventsContains(t, blockRes.TxResults[0].Events, expEvents) -} - -func TestRewardsProgramStartPerformQualifyingActions(t *testing.T) { - pioconfig.SetProvenanceConfig("nhash", 0) - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin("hotdog", 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000), - ) - app := piosimapp.SetupWithGenesisAccounts(t, "", - []authtypes.GenesisAccount{acct1}, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - rewardProgram := *rewardtypes.NewMsgCreateRewardProgramRequest( - "title", - "description", - acct1.Address, - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time.Now().Add(time.Duration(100)*time.Millisecond), - 9, - 3, - 3, - 10, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - txReward, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - &rewardProgram, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: ctx.BlockHeight() + 1, - Time: time.Now().UTC(), - Txs: [][]byte{txReward}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - - time.Sleep(110 * time.Millisecond) - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - for height := int64(2); height <= int64(99); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 10, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.NotEqual(t, "0nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 98, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 10, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 1, "RewardAccountState unspecified") - assert.Equal(t, "100nhash", byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unclaimable") - canCheck = assert.NotNil(t, byAddress, "byAddress unclaimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unclaimable") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 1, "RewardAccountState unclaimable") - assert.Equal(t, "100nhash", byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unclaimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unclaimable") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - assert.Empty(t, byAddress.RewardAccountState, "RewardAccountState claimable") -} - -func TestRewardsProgramStartPerformQualifyingActionsRecordedRewardsUnclaimable(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 1) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(50*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - // get past the reward start time ( test that reward program starts up after 50ms) - time.Sleep(55 * time.Millisecond) - - for height := int64(1); height < int64(21); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 10, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 100_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 20, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 10, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unclaimable") - canCheck = assert.NotNil(t, byAddress, "byAddress unclaimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unclaimable") - if canCheck { - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unclaimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unclaimable") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - if assert.NotNil(t, byAddress, "byAddress claimable") { - assert.Empty(t, byAddress.RewardAccountState, "RewardAccountState claimable") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimed") - if assert.NotNil(t, byAddress, "byAddress claimed") { - assert.Empty(t, byAddress.RewardAccountState, "RewardAccountState claimed") - } -} - -func TestRewardsProgramStartPerformQualifyingActionsSomePeriodsClaimableModuleAccountFunded(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 1) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - // fund th =e deterministic rewards account, since genesis won't do that work - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - banktypes.Balance{Address: "cosmos1w6t0l7z0yerj49ehnqwqaayxqpe3u7e23edgma", Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(150 * time.Millisecond) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - // wait for claim period to end (claim period is 1s) - time.Sleep(1500 * time.Millisecond) - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 1, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000), claimPeriodDistributions[0].RewardsPool, "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 1, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 1, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 5, "RewardAccountState unspecified") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - canCheck = assert.NotNil(t, byAddress, "byAddress claimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState claimable") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 4, "RewardAccountState claimable") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim claimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus claimable") - } - - // get the accoutn balances of acct1 - balance := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // claim rewards for the address - msgClaim := rewardtypes.NewMsgClaimAllRewardsRequest(acct1.Address) - require.NoError(t, acct1.SetSequence(seq), "SetSequence(%d)", seq) - txClaim, errClaim := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - fees, - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msgClaim, - ) - require.NoError(t, errClaim, "SignTxAndGetBytes") - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 6, - Time: time.Now().UTC(), - Txs: [][]byte{txClaim}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - - // unmarshal the TxMsgData - var protoResult sdk.TxMsgData - - require.NoError(t, app.AppCodec().Unmarshal(res.TxResults[0].Data, &protoResult), "unmarshalling protoResult") - require.Len(t, protoResult.MsgResponses, 1, "protoResult.MsgResponses") - require.Equal(t, protoResult.MsgResponses[0].GetTypeUrl(), "/provenance.reward.v1.MsgClaimAllRewardsResponse", - "protoResult.MsgResponses[0].GetTypeUrl()") - claimResponse := rewardtypes.MsgClaimAllRewardsResponse{} - require.NoError(t, claimResponse.Unmarshal(protoResult.MsgResponses[0].Value), "unmarshalling claimResponse") - assert.Equal(t, sdk.NewInt64Coin("nhash", 50_000_000_000).String(), claimResponse.TotalRewardClaim[0].String(), - "TotalRewardClaim") - if assert.Len(t, claimResponse.ClaimDetails, 1, "ClaimDetails") { - assert.Equal(t, 1, int(claimResponse.ClaimDetails[0].RewardProgramId), "RewardProgramId") - assert.Equal(t, sdk.NewInt64Coin("nhash", 50_000_000_000).String(), - claimResponse.ClaimDetails[0].TotalRewardClaim.String(), "ClaimDetails TotalRewardClaim") - if assert.Len(t, claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails, 5, "ClaimedRewardPeriodDetails") { - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails[0].ClaimPeriodReward.String(), "ClaimPeriodReward") - } - } - balanceLater := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // make sure account balance has the tokens - balanceChange := balanceLater.AmountOf(pioconfig.GetProvenanceConfig().FeeDenom).Sub(balance.AmountOf(pioconfig.GetProvenanceConfig().FeeDenom)) - assert.Equal(t, sdkmath.NewInt(50_000_000_000).String(), balanceChange.String(), "balance change") -} - -func TestRewardsProgramStartPerformQualifyingActionsSomePeriodsClaimableModuleAccountFundedDifferentDenom(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin("hotdog", 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("hotdog", 1000_000_000_000), - sdk.NewInt64Coin("hotdog", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - // fund th =e deterministic rewards account, since genesis won't do that work - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - banktypes.Balance{Address: "cosmos1w6t0l7z0yerj49ehnqwqaayxqpe3u7e23edgma", Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(150 * time.Millisecond) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - // wait for claim period to end (claim period is 1s) - time.Sleep(1500 * time.Millisecond) - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 1, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCount := int(rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer)) - assert.Equal(t, 1, actionCount, "ActionCounter transfer") - assert.Equal(t, 1, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 5, "RewardAccountState unspecified") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - canCheck = assert.NotNil(t, byAddress, "byAddress claimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState claimable") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 4, "claimable rewards should be 4 for this address.") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim claimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus claimable") - } - - // get the accoutn balances of acct1 - balance := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // claim rewards for the address - msgClaim := rewardtypes.NewMsgClaimAllRewardsRequest(acct1.Address) - require.NoError(t, acct1.SetSequence(seq), "SetSequence(%d)", seq) - txClaim, errClaim := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - fees, - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msgClaim, - ) - require.NoError(t, errClaim, "SignTxAndGetBytes") - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 6, - Time: time.Now().UTC(), - Txs: [][]byte{txClaim}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - - // unmarshal the TxMsgData - var protoResult sdk.TxMsgData - require.NoError(t, app.AppCodec().Unmarshal(res.TxResults[0].Data, &protoResult), "unmarshalling protoResult") - require.Len(t, protoResult.MsgResponses, 1, "protoResult.MsgResponses") - require.Equal(t, protoResult.MsgResponses[0].GetTypeUrl(), "/provenance.reward.v1.MsgClaimAllRewardsResponse", - "protoResult.MsgResponses[0].GetTypeUrl()") - claimResponse := rewardtypes.MsgClaimAllRewardsResponse{} - require.NoError(t, claimResponse.Unmarshal(protoResult.MsgResponses[0].Value), "unmarshalling claimResponse") - if assert.NotEmpty(t, claimResponse.TotalRewardClaim, "TotalRewardClaim") { - assert.Equal(t, sdk.NewInt64Coin("hotdog", 50_000_000_000).String(), - claimResponse.TotalRewardClaim[0].String(), "TotalRewardClaim") - } - if assert.NotEmpty(t, claimResponse.ClaimDetails, "ClaimDetails") { - assert.Len(t, claimResponse.ClaimDetails, 1, "ClaimDetails") - assert.Equal(t, 1, int(claimResponse.ClaimDetails[0].RewardProgramId), "RewardProgramId") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 50_000_000_000).String(), - claimResponse.ClaimDetails[0].TotalRewardClaim.String(), "ClaimDetails TotalRewardClaim") - if assert.NotEmpty(t, claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails, "ClaimedRewardPeriodDetails") { - assert.Len(t, claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails, 5, "ClaimedRewardPeriodDetails") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails[0].ClaimPeriodReward.String(), "ClaimPeriodReward") - } - } - - app.Commit() - balanceLater := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - balanceChange := balanceLater.AmountOf("hotdog").Sub(balance.AmountOf("hotdog")) - // make sure account balance has the tokens - assert.Equal(t, sdkmath.NewInt(50_000_000_000).String(), balanceChange.String(), "balance change") -} - -func TestRewardsProgramStartPerformQualifyingActionsSomePeriodsClaimableModuleAccountFundedDifferentDenomClaimedTogether(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin("hotdog", 1000000_000_000_000), - sdk.NewInt64Coin("nhash", 1000000_000_000_000), - ) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("hotdog", 1000_000_000_000), - sdk.NewInt64Coin("hotdog", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - - secondRewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 2, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - // fund th =e deterministic rewards account, since genesis won't do that work - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(3), []rewardtypes.RewardProgram{rewardProgram, secondRewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - banktypes.Balance{Address: "cosmos1w6t0l7z0yerj49ehnqwqaayxqpe3u7e23edgma", Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(150 * time.Millisecond) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - - // wait for claim period to end (claim period is 1s) - time.Sleep(1500 * time.Millisecond) - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 1, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 1, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 1, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 10, "RewardAccountState unspecified") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - canCheck = assert.NotNil(t, byAddress, "byAddress claimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState claimable") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 8, "RewardAccountState claimable") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim claimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus claimable") - } - - // get the accoutn balances of acct1 - balance := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // claim rewards for the address - - msgClaim := rewardtypes.NewMsgClaimAllRewardsRequest(acct1.Address) - require.NoError(t, acct1.SetSequence(seq), "SetSequence(%d)", seq) - // needs extra gas - txClaim, errClaim := SignTxAndGetBytes(ctx, 300000, fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msgClaim) - require.NoError(t, errClaim, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 6, - Time: time.Now().UTC(), - Txs: [][]byte{txClaim}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - - // unmarshal the TxMsgData - var protoResult sdk.TxMsgData - require.NoError(t, app.AppCodec().Unmarshal(res.TxResults[0].Data, &protoResult), "unmarshalling protoResult") - require.Len(t, protoResult.MsgResponses, 1, "protoResult.MsgResponses") - require.Equal(t, protoResult.MsgResponses[0].GetTypeUrl(), "/provenance.reward.v1.MsgClaimAllRewardsResponse", - "protoResult.MsgResponses[0].GetTypeUrl()") - claimResponse := rewardtypes.MsgClaimAllRewardsResponse{} - require.NoError(t, claimResponse.Unmarshal(protoResult.MsgResponses[0].Value), "unmarshalling claimResponse") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 50_000_000_000).String(), - claimResponse.TotalRewardClaim[0].String(), "TotalRewardClaim") - if assert.NotEmpty(t, claimResponse.ClaimDetails, "ClaimDetails") { - assert.Len(t, claimResponse.ClaimDetails, 2) - - assert.Equal(t, 1, int(claimResponse.ClaimDetails[0].RewardProgramId), "[0].RewardProgramId") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 50_000_000_000).String(), - claimResponse.ClaimDetails[0].TotalRewardClaim.String(), "[0].TotalRewardClaim") - if assert.NotEmpty(t, claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails, "[0].ClaimedRewardPeriodDetails") { - assert.Len(t, claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails, 5, "[0].ClaimedRewardPeriodDetails") - assert.Equal(t, sdk.NewInt64Coin("hotdog", 10_000_000_000).String(), - claimResponse.ClaimDetails[0].ClaimedRewardPeriodDetails[0].ClaimPeriodReward.String(), "[0].[0].ClaimPeriodReward") - } - - assert.Equal(t, 2, int(claimResponse.ClaimDetails[1].RewardProgramId), "[1].RewardProgramId") - assert.Equal(t, sdk.NewInt64Coin("nhash", 50_000_000_000).String(), - claimResponse.ClaimDetails[1].TotalRewardClaim.String(), "[1].TotalRewardClaim") - if assert.NotEmpty(t, claimResponse.ClaimDetails[1].ClaimedRewardPeriodDetails, "[1].ClaimedRewardPeriodDetails") { - assert.Len(t, claimResponse.ClaimDetails[1].ClaimedRewardPeriodDetails, 5, "[1].ClaimedRewardPeriodDetails") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimResponse.ClaimDetails[1].ClaimedRewardPeriodDetails[0].ClaimPeriodReward.String(), "[1].[0].ClaimPeriodReward") - } - } - - _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 7, - Time: time.Now().UTC(), - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - - balanceLater := app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // make sure account balance has the tokens - balanceChangeHotDog := balanceLater.AmountOf("hotdog").Sub(balance.AmountOf("hotdog")) - assert.Equal(t, sdkmath.NewInt(50_000_000_000).String(), balanceChangeHotDog.String(), "balance change hotdog") - - balanceLater = app.BankKeeper.GetAllBalances(ctx, acct1.GetAddress()) - // make sure account balance has the tokens - balanceChangeNHash := balanceLater.AmountOf("nhash").Sub(balance.AmountOf("nhash")) - assert.Equal(t, sdkmath.NewInt(50_000_000_000).String(), balanceChangeNHash.String(), "balance change nhash") -} - -func TestRewardsProgramStartPerformQualifyingActionsSomePeriodsClaimableModuleAccountNotFunded(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - // do not fund the deterministic rewards account - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(150 * time.Millisecond) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - - time.Sleep(1500 * time.Millisecond) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 1, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 1, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 1, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress unspecified") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState unspecified") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 5, "RewardAccountState unspecified") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim unspecified") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus unspecified") - } - - byAddress, err = app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress claimable") - canCheck = assert.NotNil(t, byAddress, "byAddress claimable") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState claimable") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 4, "RewardAccountState claimable") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim claimable") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus claimable") - } - - // claim rewards for the address - msgClaim := rewardtypes.NewMsgClaimAllRewardsRequest(acct1.Address) - require.NoError(t, acct1.SetSequence(seq), "SetSequence(%d)", seq) - txClaim, errClaim := SignTxAndGetBytes(ctx, - NewTestRewardsGasLimit(), - fees, - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msgClaim, - ) - require.NoError(t, errClaim, "SignTxAndGetBytes") - - _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 6, - Time: time.Now().UTC(), - Txs: [][]byte{txClaim}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() -} - -// Checks to see if delegation are met for a Qualifying action in this case Transfer -// this tests has transfers from an account which DOES NOT have the minimum delegation -// amount needed to get a share -func TestRewardsProgramStartPerformQualifyingActionsCriteriaNotMet(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - _, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.Coin{ - Denom: "nhash", - Amount: sdkmath.NewInt(10_000_000_000)}, - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(110 * time.Millisecond) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - time.Sleep(1100 * time.Millisecond) - - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 0, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 0, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 0, int(accountState.SharesEarned), "SharesEarned") -} - -// Checks to see if delegation are met for a Qualifying action in this case, Transfer, create an address with delegations -// transfers which map to QualifyingAction map to the delegated address -// delegation threshold is met -func TestRewardsProgramStartPerformQualifyingActionsTransferAndDelegationsPresent(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.Coin{ - Denom: "nhash", - Amount: sdkmath.NewInt(100)}, - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, createValSet(t, pubKey, pubKey2), - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - // wait for program to start - time.Sleep(150 * time.Millisecond) - - //go through 5 blocks, but take a time to cut blocks > claim period time interval. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - time.Sleep(1100 * time.Millisecond) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 1, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 1, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 1, int(accountState.SharesEarned), "account state incorrect") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress") - canCheck := assert.NotNil(t, byAddress, "byAddress unspecified") - canCheck = canCheck && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState") - if canCheck { - assert.Len(t, byAddress.RewardAccountState, 5, "RewardAccountState") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus") - } -} - -// Checks to see if delegation are met for a Qualifying action in this case Transfer, create an address with delegations -// transfers which map to QualifyingAction map to the delegated address -// delegation threshold is *not* met -func TestRewardsProgramStartPerformQualifyingActionsThreshHoldNotMet(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(1), - 100, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Transfer{ - Transfer: &rewardtypes.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.Coin{ - Denom: "nhash", - Amount: sdkmath.NewInt(100_000_000)}, - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, createValSet(t, pubKey, pubKey2), - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - - // tx with a fee associated with msg type and account has funds - msg := banktypes.NewMsgSend(addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("atom", 50))) - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(1 * time.Second) - - //go through 5 blocks, but take a long time to cut blocks. - for height := int64(1); height < int64(6); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - time.Sleep(1100 * time.Millisecond) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Equal(t, 0, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, true, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeTransfer) - assert.Equal(t, 0, int(actionCounter), "ActionCounter transfer") - assert.Equal(t, 0, int(accountState.SharesEarned), "SharesEarned") -} - -func TestRewardsProgramStartPerformQualifyingActions_Vote(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, _, addr := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000), - ) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Vote{ - Vote: &rewardtypes.ActionVote{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, nil, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - - txGov, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msg, - ) - require.NoError(t, err, "SignTxAndGetBytes") - time.Sleep(200 * time.Millisecond) - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - app.Commit() - - seq = seq + 1 - proposal := getLastProposal(t, ctx, app) - - // tx with a fee associated with msg type and account has funds - vote1 := govtypesv1beta1.NewMsgVote(addr, proposal.Id, govtypesv1beta1.OptionYes) - - for height := int64(2); height < int64(22); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), vote1) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 10, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.NotEqual(t, "0nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeVote) - assert.Equal(t, 20, int(actionCounter), "ActionCounter vote") - assert.Equal(t, 10, int(accountState.SharesEarned), "SharesEarned") -} - -func TestRewardsProgramStartPerformQualifyingActions_Vote_InvalidDelegations(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv1, pubKey1, addr1 := testdata.KeyTestPubAddr() - priv2, _, addr2 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr1, priv1.PubKey(), 0, 0) - acct2 := authtypes.NewBaseAccount(addr2, priv2.PubKey(), 1, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acctBalance := sdk.NewCoins( - sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000), - ) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Vote{ - Vote: &rewardtypes.ActionVote{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 1000), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1, acct2}, createValSet(t, pubKey1), - banktypes.Balance{Address: addr1.String(), Coins: acctBalance}, - banktypes.Balance{Address: addr2.String(), Coins: acctBalance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct2.GetAddress(), fundCoins), - "funding acct2 with 290500010nhash") - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr1, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - time.Sleep(200 * time.Millisecond) - - txGov, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv1.PubKey(), priv1, *acct1, ctx.ChainID(), - msg, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - proposal := getLastProposal(t, ctx, app) - - // tx with a fee associated with msg type and account has funds - vote2 := govtypesv1beta1.NewMsgVote(addr2, proposal.Id, govtypesv1beta1.OptionYes) - acct2 = app.AccountKeeper.GetAccount(ctx, acct2.GetAddress()).(*authtypes.BaseAccount) - seq := acct2.Sequence - - for height := int64(2); height < int64(4); height++ { - require.NoError(t, acct2.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv2.PubKey(), priv2, *acct2, ctx.ChainID(), vote2) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 0, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.Equal(t, "100000000000nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct2.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeVote) - assert.Equal(t, 0, int(actionCounter), "ActionCounter vote") - assert.Equal(t, 0, int(accountState.SharesEarned), "SharesEarned") - - byAddress1, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress acct1") - assert.Empty(t, byAddress1.RewardAccountState, "RewardAccountState acct1") - - byAddress2, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct2.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress acct2") - assert.Empty(t, byAddress2.RewardAccountState, "RewardAccountState acct2") -} - -func TestRewardsProgramStartPerformQualifyingActions_Vote_ValidDelegations(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, _ := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000), - ) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Vote{ - Vote: &rewardtypes.ActionVote{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 1000), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, createValSet(t, pubKey, pubKey2), - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(200 * time.Millisecond) - - txGov, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msg, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - seq = seq + 1 - proposal := getLastProposal(t, ctx, app) - - // tx with a fee associated with msg type and account has funds - vote1 := govtypesv1beta1.NewMsgVote(addr, proposal.Id, govtypesv1beta1.OptionYes) - - // threshold will be met after 10 actions - for height := int64(2); height < int64(22); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), vote1) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 10, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.NotEqual(t, "0nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeVote) - assert.Equal(t, 20, int(actionCounter), "ActionCounter vote") - assert.Equal(t, 10, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress") - if assert.NotNil(t, byAddress, "byAddress") && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState") { - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus") - } -} - -// checks to see that votes are coming from an address that has delegated enough coins and is a validator, and gets the multiplier applied -func TestRewardsProgramStartPerformQualifyingActions_Vote_ValidDelegations_Multiplier_Present(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, _ := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin("nhash", 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Vote{ - Vote: &rewardtypes.ActionVote{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 1000), - ValidatorMultiplier: 10, - }, - }, - }, - }, - ) - - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - //err, bondedVal1, bondedVal2 := createTestValidators(pubKey, pubKey2, addr, addr2) - app := piosimapp.SetupWithGenesisRewardsProgram(t, uint64(2), []rewardtypes.RewardProgram{rewardProgram}, []authtypes.GenesisAccount{acct1}, createValSet(t, pubKey, pubKey2), banktypes.Balance{Address: addr.String(), Coins: acct1Balance}) - - ctx := app.BaseApp.NewContext(false) - ctx.WithBlockTime(time.Now()) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin("nhash", 290_500_010)))) - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - ctx.WithBlockTime(time.Now()) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - ctx.WithBlockTime(time.Now()) - time.Sleep(200 * time.Millisecond) - - txGov, err := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin("nhash", 1_190_500_000)), encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), msg) - require.NoError(t, err, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - seq = seq + 1 - proposal := getLastProposal(t, ctx, app) - - // tx with a fee associated with msg type and account has funds - vote1 := govtypesv1beta1.NewMsgVote(addr, proposal.Id, govtypesv1beta1.OptionYes) - - assert.NotEmpty(t, res.GetEvents(), "should have emitted an event.") - - // threshold will be met after 10 actions - for height := int64(2); height < int64(22); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), vote1) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - app.Commit() - seq = seq + 1 - } - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err) - assert.Len(t, claimPeriodDistributions, 1, "claim period reward distributions should exist") - assert.Equal(t, int64(100), claimPeriodDistributions[0].TotalShares, "shares should have accumulated to value of 100 ( 10 action * multiplier (10) shares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "claim period has not ended.") - assert.Equal(t, false, claimPeriodDistributions[0].RewardsPool.Equal(sdk.Coin{ - Denom: "nhash", - Amount: sdkmath.ZeroInt(), - }), "claim period has not ended so rewards still haven't been calculated(hence 0 coins)") - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err) - assert.Equal(t, uint64(20), rewardtypes.GetActionCount(accountState.ActionCounter, "ActionVote"), "account state incorrect") - assert.Equal(t, 100, int(accountState.SharesEarned), "account state incorrect") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }) - require.NoError(t, err) - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), byAddress.RewardAccountState[0].TotalRewardClaim.String(), "RewardDistributionsByAddress incorrect") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, byAddress.RewardAccountState[0].ClaimStatus, "claim status incorrect") -} - -// checks to see that votes are coming from an address that has delegated enough coins but is not a validator, hence does not get the multiplier applied -func TestRewardsProgramStartPerformQualifyingActions_Vote_ValidDelegations_Multiplier_Present_But_NotValidatorVotes(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, _ := testdata.KeyTestPubAddr() - // an address which is not a validator - priv3, _, addr3 := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - acct3 := authtypes.NewBaseAccount(addr3, priv3.PubKey(), 0, 0) - acct1Balance := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), sdk.NewInt64Coin("atom", 10000000), sdk.NewInt64Coin("nhash", 1000000_000_000_000)) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Vote{ - Vote: &rewardtypes.ActionVote{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 1000), - ValidatorMultiplier: 10, - }, - }, - }, - }, - ) - - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - app := piosimapp.SetupWithGenesisRewardsProgram(t, uint64(2), []rewardtypes.RewardProgram{rewardProgram}, []authtypes.GenesisAccount{acct3}, createValSet(t, pubKey, pubKey2), banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, banktypes.Balance{Address: addr3.String(), Coins: acct1Balance}) - - ctx := app.BaseApp.NewContext(false) - ctx.WithBlockTime(time.Now()) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin("nhash", 290_500_010)))) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct3.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin("nhash", 290_500_010)))) - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr3, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - ctx.WithBlockTime(time.Now()) - acct3 = app.AccountKeeper.GetAccount(ctx, acct3.GetAddress()).(*authtypes.BaseAccount) - seq := acct3.Sequence - ctx.WithBlockTime(time.Now()) - time.Sleep(200 * time.Millisecond) - - txGov, err := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin("nhash", 1_190_500_000)), encCfg, priv3.PubKey(), priv3, *acct3, ctx.ChainID(), msg) - require.NoError(t, err, "SignTxAndGetBytes") - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - seq = seq + 1 - proposal := getLastProposal(t, ctx, app) - - // vote with an account which is not a validator - vote := govtypesv1beta1.NewMsgVote(addr3, proposal.Id, govtypesv1beta1.OptionYes) - - assert.NotEmpty(t, res.GetEvents(), "should have emitted an event.") - - // threshold will be met after 10 actions - for height := int64(2); height < int64(22); height++ { - require.NoError(t, acct3.SetSequence(seq)) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv3.PubKey(), priv3, *acct3, ctx.ChainID(), vote) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - app.Commit() - seq = seq + 1 - } - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err) - assert.Len(t, claimPeriodDistributions, 1, "claim period reward distributions should exist") - assert.Equal(t, int64(10), claimPeriodDistributions[0].TotalShares, "shares should have accumulated to value of 10, ( 10 action leading to 1 share each) (no multiplier is applied) ") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "claim period has not ended.") - assert.Equal(t, false, claimPeriodDistributions[0].RewardsPool.Equal(sdk.Coin{ - Denom: "nhash", - Amount: sdkmath.ZeroInt(), - }), "claim period has not ended so rewards still haven't been calculated(hence 0 coins)") - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct3.Address) - require.NoError(t, err) - assert.Equal(t, uint64(20), rewardtypes.GetActionCount(accountState.ActionCounter, "ActionVote"), "account state incorrect") - assert.Equal(t, 10, int(accountState.SharesEarned), "account state incorrect") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct3.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }) - require.NoError(t, err) - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), byAddress.RewardAccountState[0].TotalRewardClaim.String(), "RewardDistributionsByAddress incorrect") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, byAddress.RewardAccountState[0].ClaimStatus, "claim status incorrect") -} - -func TestRewardsProgramStartPerformQualifyingActions_Delegate_NoQualifyingActions(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, _ := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000), - ) - - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Delegate{ - Delegate: &rewardtypes.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: nil, - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, createValSet(t, pubKey, pubKey2), - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(110 * time.Millisecond) - - txGov, err := SignTxAndGetBytes( - ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msg, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - seq = seq + 1 - proposal := getLastProposal(t, ctx, app) - - // tx with a fee associated with msg type and account has funds - vote1 := govtypesv1beta1.NewMsgVote(addr, proposal.Id, govtypesv1beta1.OptionYes) - - for height := int64(2); height < int64(14); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), vote1) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 0, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.NotEqual(t, "0nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeVote) - assert.Equal(t, 0, int(actionCounter), "ActionCounter vote") - assert.Equal(t, 0, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress") - if assert.NotNil(t, byAddress, "byAddress") { - assert.Empty(t, byAddress.RewardAccountState, "RewardAccountState") - } -} - -func TestRewardsProgramStartPerformQualifyingActions_Delegate_QualifyingActionsPresent(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig() - priv, pubKey, addr := testdata.KeyTestPubAddr() - _, pubKey2, _ := testdata.KeyTestPubAddr() - acct1 := authtypes.NewBaseAccount(addr, priv.PubKey(), 0, 0) - pioconfig.SetProvenanceConfig("nhash", 0) - acct1Balance := sdk.NewCoins( - sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000000), - sdk.NewInt64Coin("atom", 10000000), - sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1000000_000_000_000), - ) - minDelegation := sdk.NewInt64Coin("nhash", 4) - maxDelegation := sdk.NewInt64Coin("nhash", 2001000) - rewardProgram := rewardtypes.NewRewardProgram( - "title", - "description", - 1, - acct1.Address, - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []rewardtypes.QualifyingAction{ - { - Type: &rewardtypes.QualifyingAction_Delegate{ - Delegate: &rewardtypes.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(100, 0), - }, - }, - }, - }, - ) - rewardProgram.State = rewardtypes.RewardProgram_STATE_PENDING - - valSet := createValSet(t, pubKey, pubKey2) - app := piosimapp.SetupWithGenesisRewardsProgram(t, - uint64(2), []rewardtypes.RewardProgram{rewardProgram}, - []authtypes.GenesisAccount{acct1}, valSet, - banktypes.Balance{Address: addr.String(), Coins: acct1Balance}, - ) - - ctx := app.BaseApp.NewContext(false) - require.NoError(t, app.AccountKeeper.Params.Set(ctx, authtypes.DefaultParams()), "Setting default account params") - fundCoins := sdk.NewCoins(sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 290_500_010)) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, acct1.GetAddress(), fundCoins), - "funding acct1 with 290500010nhash") - coinsPos := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000)) - msg, err := govtypesv1beta1.NewMsgSubmitProposal( - ContentFromProposalType("title", "description", govtypesv1beta1.ProposalTypeText), - coinsPos, - addr, - ) - require.NoError(t, err, "NewMsgSubmitProposal expected to create msg") - - fees := sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) - acct1 = app.AccountKeeper.GetAccount(ctx, acct1.GetAddress()).(*authtypes.BaseAccount) - seq := acct1.Sequence - time.Sleep(200 * time.Millisecond) - - txGov, err := SignTxAndGetBytes(ctx, - NewTestRewardsGasLimit(), - sdk.NewCoins(sdk.NewInt64Coin("atom", 150), sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().FeeDenom, 1_190_500_000)), - encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), - msg, - ) - require.NoError(t, err, "SignTxAndGetBytes") - - res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: 1, - Time: time.Now().UTC(), - Txs: [][]byte{txGov}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - - app.Commit() - - seq = seq + 1 - _ = getLastProposal(t, ctx, app) // Just making sure it exists. - - // tx with a fee associated with msg type and account has funds - delAddr, _ := valSet.GetByIndex(0) - delegation := stakingtypes.NewMsgDelegate(addr.String(), sdk.ValAddress(delAddr).String(), sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000)) - - for height := int64(2); height < int64(22); height++ { - require.NoError(t, acct1.SetSequence(seq), "[%d]: SetSequence(%d)", height, seq) - tx1, err1 := SignTxAndGetBytes(ctx, NewTestRewardsGasLimit(), fees, encCfg, priv.PubKey(), priv, *acct1, ctx.ChainID(), delegation) - require.NoError(t, err1, "[%d]: SignTxAndGetBytes", height) - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{ - Height: height, - Time: time.Now().UTC(), - Txs: [][]byte{tx1}, - }, - ) - require.NoError(t, err, "FinalizeBlock expected no error") - require.Len(t, res.TxResults, 1, "TxResults expected length not met") - require.Equal(t, uint32(0), res.TxResults[0].Code, "TxResults tx unexpected result code "+res.TxResults[0].Log) - time.Sleep(100 * time.Millisecond) - - app.Commit() - seq = seq + 1 - } - - claimPeriodDistributions, err := app.RewardKeeper.GetAllClaimPeriodRewardDistributions(ctx) - require.NoError(t, err, "GetAllClaimPeriodRewardDistributions") - if assert.NotEmpty(t, claimPeriodDistributions, "claimPeriodDistributions") { - assert.Len(t, claimPeriodDistributions, 1, "claimPeriodDistributions") - assert.Equal(t, 10, int(claimPeriodDistributions[0].TotalShares), "TotalShares") - assert.Equal(t, false, claimPeriodDistributions[0].ClaimPeriodEnded, "ClaimPeriodEnded") - assert.NotEqual(t, "0nhash", claimPeriodDistributions[0].RewardsPool.String(), "RewardsPool") - } - - accountState, err := app.RewardKeeper.GetRewardAccountState(ctx, uint64(1), uint64(1), acct1.Address) - require.NoError(t, err, "GetRewardAccountState") - actionCounter := rewardtypes.GetActionCount(accountState.ActionCounter, rewardtypes.ActionTypeDelegate) - assert.Equal(t, 20, int(actionCounter), "ActionCounter delegate") - assert.Equal(t, 10, int(accountState.SharesEarned), "SharesEarned") - - byAddress, err := app.RewardKeeper.RewardDistributionsByAddress(sdk.WrapSDKContext(ctx), - &rewardtypes.QueryRewardDistributionsByAddressRequest{ - Address: acct1.Address, - ClaimStatus: rewardtypes.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - }, - ) - require.NoError(t, err, "RewardDistributionsByAddress") - if assert.NotNil(t, byAddress, "byAddress") && assert.NotEmpty(t, byAddress.RewardAccountState, "RewardAccountState") { - assert.Equal(t, sdk.NewInt64Coin("nhash", 10_000_000_000).String(), - byAddress.RewardAccountState[0].TotalRewardClaim.String(), "TotalRewardClaim") - assert.Equal(t, rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, - byAddress.RewardAccountState[0].ClaimStatus, "ClaimStatus") - } -} - // ContentFromProposalType returns a Content object based on the proposal type. func ContentFromProposalType(title, desc, ty string) govtypesv1beta1.Content { switch ty { @@ -3433,10 +1111,6 @@ func NewTestGasLimit() uint64 { return 150000 } -func NewTestRewardsGasLimit() uint64 { - return 200000 -} - // CreateSendCoinEvents creates the sequence of events that are created on bankkeeper.SendCoins func CreateSendCoinEvents(fromAddress, toAddress string, amt sdk.Coins) []abci.Event { events := sdk.NewEventManager().Events() diff --git a/internal/provwasm/stargate_whitelist.go b/internal/provwasm/stargate_whitelist.go index 185b8b1c23..f03b4859e9 100644 --- a/internal/provwasm/stargate_whitelist.go +++ b/internal/provwasm/stargate_whitelist.go @@ -22,7 +22,6 @@ import ( metadatatypes "github.com/provenance-io/provenance/x/metadata/types" msgfeestypes "github.com/provenance-io/provenance/x/msgfees/types" nametypes "github.com/provenance-io/provenance/x/name/types" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" triggertypes "github.com/provenance-io/provenance/x/trigger/types" ) @@ -135,13 +134,6 @@ func init() { setWhitelistedQuery("/provenance.name.v1.Query/Resolve", &nametypes.QueryResolveResponse{}) setWhitelistedQuery("/provenance.name.v1.Query/ReverseLookup", &nametypes.QueryReverseLookupResponse{}) - // reward - setWhitelistedQuery("/provenance.reward.v1.Query/RewardProgramByID", &rewardtypes.QueryRewardProgramByIDResponse{}) - setWhitelistedQuery("/provenance.reward.v1.Query/RewardPrograms", &rewardtypes.QueryRewardProgramsResponse{}) - setWhitelistedQuery("/provenance.reward.v1.Query/ClaimPeriodRewardDistributions", &rewardtypes.QueryClaimPeriodRewardDistributionsResponse{}) - setWhitelistedQuery("/provenance.reward.v1.Query/ClaimPeriodRewardDistributionsByID", &rewardtypes.QueryClaimPeriodRewardDistributionsByIDResponse{}) - setWhitelistedQuery("/provenance.reward.v1.Query/RewardDistributionsByAddress", &rewardtypes.QueryRewardDistributionsByAddressResponse{}) - // trigger setWhitelistedQuery("/provenance.trigger.v1.Query/TriggerByID", &triggertypes.QueryTriggerByIDResponse{}) setWhitelistedQuery("/provenance.trigger.v1.Query/Triggers", &triggertypes.QueryTriggersResponse{}) diff --git a/proto/provenance/name/v1/name.proto b/proto/provenance/name/v1/name.proto index 8e7068b05a..561f0b8aec 100644 --- a/proto/provenance/name/v1/name.proto +++ b/proto/provenance/name/v1/name.proto @@ -31,7 +31,8 @@ message NameRecord { // the bound name string name = 1; // the address the name resolved to - string address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];; + string address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + ; // whether owner signature is required to add sub-names bool restricted = 3; } diff --git a/proto/provenance/reward/v1/genesis.proto b/proto/provenance/reward/v1/genesis.proto deleted file mode 100644 index 52bf12a12f..0000000000 --- a/proto/provenance/reward/v1/genesis.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; -package provenance.reward.v1; - -import "gogoproto/gogo.proto"; -import "provenance/reward/v1/reward.proto"; - -option go_package = "github.com/provenance-io/provenance/x/reward/types"; -option java_package = "io.provenance.reward.v1"; -option java_multiple_files = true; - -// GenesisState defines the reward module's genesis state. -message GenesisState { - option (gogoproto.equal) = false; - option (gogoproto.goproto_getters) = false; - - // Reward program id is the next auto incremented id to be assigned to the next created reward program - uint64 reward_program_id = 1; - // Reward programs to initially start with. - repeated RewardProgram reward_programs = 2 [(gogoproto.nullable) = false]; - // Claim period reward distributions to initially start with. - repeated ClaimPeriodRewardDistribution claim_period_reward_distributions = 3 [(gogoproto.nullable) = false]; - // Reward account states to initially start with. - repeated RewardAccountState reward_account_states = 4 [(gogoproto.nullable) = false]; -} diff --git a/proto/provenance/reward/v1/query.proto b/proto/provenance/reward/v1/query.proto deleted file mode 100644 index 38e905c494..0000000000 --- a/proto/provenance/reward/v1/query.proto +++ /dev/null @@ -1,152 +0,0 @@ -syntax = "proto3"; -package provenance.reward.v1; - -import "gogoproto/gogo.proto"; -import "google/api/annotations.proto"; -import "cosmos/base/v1beta1/coin.proto"; -import "cosmos/base/query/v1beta1/pagination.proto"; -import "provenance/reward/v1/reward.proto"; - -option go_package = "github.com/provenance-io/provenance/x/reward/types"; -option java_package = "io.provenance.reward.v1"; -option java_multiple_files = true; - -// Query defines the gRPC querier service for reward module. -service Query { - - // RewardProgramByID returns a reward program matching the ID. - rpc RewardProgramByID(QueryRewardProgramByIDRequest) returns (QueryRewardProgramByIDResponse) { - option (google.api.http).get = "/provenance/rewards/v1/reward_programs/{id}"; - } - // RewardPrograms returns a list of reward programs matching the query type. - rpc RewardPrograms(QueryRewardProgramsRequest) returns (QueryRewardProgramsResponse) { - option (google.api.http).get = "/provenance/rewards/v1/reward_programs"; - } - - // ClaimPeriodRewardDistributions returns a list of claim period reward distributions matching the claim_status. - rpc ClaimPeriodRewardDistributions(QueryClaimPeriodRewardDistributionsRequest) - returns (QueryClaimPeriodRewardDistributionsResponse) { - option (google.api.http).get = "/provenance/rewards/v1/claim_period_reward_distributions"; - } - - // ClaimPeriodRewardDistributionsByID returns a claim period reward distribution matching the ID. - rpc ClaimPeriodRewardDistributionsByID(QueryClaimPeriodRewardDistributionsByIDRequest) - returns (QueryClaimPeriodRewardDistributionsByIDResponse) { - option (google.api.http).get = - "/provenance/rewards/v1/claim_period_reward_distributions/{reward_id}/claim_periods/{claim_period_id}"; - } - - // RewardDistributionsByAddress returns a list of reward claims belonging to the account and matching the claim - // status. - rpc RewardDistributionsByAddress(QueryRewardDistributionsByAddressRequest) - returns (QueryRewardDistributionsByAddressResponse) { - option (google.api.http).get = "/provenance/rewards/v1/reward_claims/{address}"; - } -} - -// QueryRewardProgramByIDRequest queries for the Reward Program with an identifier of id -message QueryRewardProgramByIDRequest { - // The id of the reward program to query. - uint64 id = 1; -} - -// QueryRewardProgramByIDResponse contains the requested RewardProgram -message QueryRewardProgramByIDResponse { - // The reward program object that was queried for. - RewardProgram reward_program = 1; -} - -// QueryRewardProgramsRequest queries for all reward programs matching the query_type -message QueryRewardProgramsRequest { - // QueryType is the state of reward program to query - enum QueryType { - // unspecified type - QUERY_TYPE_UNSPECIFIED = 0; - // all reward programs states - QUERY_TYPE_ALL = 1; - // pending reward program state= - QUERY_TYPE_PENDING = 2; - // active reward program state - QUERY_TYPE_ACTIVE = 3; - // pending and active reward program states - QUERY_TYPE_OUTSTANDING = 4; - // finished reward program state - QUERY_TYPE_FINISHED = 5; - } - // A filter on the types of reward programs. - QueryType query_type = 1; - // pagination defines an optional pagination for the request. - cosmos.base.query.v1beta1.PageRequest pagination = 99; -} - -// QueryRewardProgramsResponse contains the list of RewardPrograms matching the query -message QueryRewardProgramsResponse { - // List of RewardProgram objects matching the query_type. - repeated RewardProgram reward_programs = 1 [(gogoproto.nullable) = false]; - // pagination defines an optional pagination for the response. - cosmos.base.query.v1beta1.PageResponse pagination = 99; -} - -// QueryClaimPeriodRewardDistributionsRequest queries for all the ClaimPeriodRewardDistributions with pagination. -message QueryClaimPeriodRewardDistributionsRequest { - // pagination defines an optional pagination for the request. - cosmos.base.query.v1beta1.PageRequest pagination = 99; -} - -// QueryClaimPeriodRewardDistributionsResponse returns the list of paginated ClaimPeriodRewardDistributions -message QueryClaimPeriodRewardDistributionsResponse { - // List of all ClaimPeriodRewardDistribution objects queried for. - repeated ClaimPeriodRewardDistribution claim_period_reward_distributions = 1 [(gogoproto.nullable) = false]; - // pagination defines an optional pagination for the response. - cosmos.base.query.v1beta1.PageResponse pagination = 99; -} - -// QueryClaimPeriodRewardDistributionsByIDRequest queries for a single ClaimPeriodRewardDistribution -message QueryClaimPeriodRewardDistributionsByIDRequest { - // The reward program that the claim period reward distribution belongs to. - uint64 reward_id = 1; - // The claim period that the claim period reward distribution was created for. - uint64 claim_period_id = 2; -} - -// QueryClaimPeriodRewardDistributionsByIDResponse returns the requested ClaimPeriodRewardDistribution -message QueryClaimPeriodRewardDistributionsByIDResponse { - // The ClaimPeriodRewardDistribution object that was queried for. - ClaimPeriodRewardDistribution claim_period_reward_distribution = 1; -} - -// QueryRewardDistributionsByAddressRequest queries for reward claims by address that match the claim_status. -message QueryRewardDistributionsByAddressRequest { - // The address that the claim belongs to. - string address = 1; - // The status that the reward account must have. - RewardAccountState.ClaimStatus claim_status = 2; - // pagination defines an optional pagination for the request. - cosmos.base.query.v1beta1.PageRequest pagination = 99; -} - -// QueryRewardDistributionsByAddressResponse returns the reward claims for an address that match the claim_status. -message QueryRewardDistributionsByAddressResponse { - // The address that the reward account belongs to. - string address = 1; - // List of RewardAccounts queried for. - repeated RewardAccountResponse reward_account_state = 2 [(gogoproto.nullable) = false]; - // pagination defines an optional pagination for the response. - cosmos.base.query.v1beta1.PageResponse pagination = 99; -} - -// RewardAccountResponse is an address' reward claim for a reward program's claim period. -message RewardAccountResponse { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // The id of the reward program that this claim belongs to. - uint64 reward_program_id = 1; - - // total rewards claimed for all eligible claim periods in program. - cosmos.base.v1beta1.Coin total_reward_claim = 2 [(gogoproto.nullable) = false]; - // The status of the claim. - RewardAccountState.ClaimStatus claim_status = 3; - // The claim period that the claim belongs to. - uint64 claim_id = 4; -} diff --git a/proto/provenance/reward/v1/reward.proto b/proto/provenance/reward/v1/reward.proto deleted file mode 100644 index 494b3e5579..0000000000 --- a/proto/provenance/reward/v1/reward.proto +++ /dev/null @@ -1,210 +0,0 @@ -syntax = "proto3"; -package provenance.reward.v1; - -import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; -import "cosmos/base/v1beta1/coin.proto"; -import "cosmos_proto/cosmos.proto"; - -option go_package = "github.com/provenance-io/provenance/x/reward/types"; -option java_package = "io.provenance.reward.v1"; -option java_multiple_files = true; - -// RewardProgram -message RewardProgram { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - // State is the state of the reward program - enum State { - // undefined program state - STATE_UNSPECIFIED = 0; - // pending state of reward program - STATE_PENDING = 1; - // started state of reward program - STATE_STARTED = 2; - // finished state of reward program - STATE_FINISHED = 3; - // expired state of reward program - STATE_EXPIRED = 4; - } - - // An integer to uniquely identify the reward program. - uint64 id = 1; - // Name to help identify the Reward Program.(MaxTitleLength=140) - string title = 2; - // Short summary describing the Reward Program.(MaxDescriptionLength=10000) - string description = 3; - // address that provides funds for the total reward pool. - string distribute_from_address = 4; - // The total amount of funding given to the RewardProgram. - cosmos.base.v1beta1.Coin total_reward_pool = 5 [(gogoproto.nullable) = false]; - // The remaining funds available to distribute after n claim periods have passed. - cosmos.base.v1beta1.Coin remaining_pool_balance = 6 [(gogoproto.nullable) = false]; - // The total amount of all funds claimed by participants for all past claim periods. - cosmos.base.v1beta1.Coin claimed_amount = 7 [(gogoproto.nullable) = false]; - // Maximum reward per claim period per address. - cosmos.base.v1beta1.Coin max_reward_by_address = 8 [(gogoproto.nullable) = false]; - // Minimum amount of coins for a program to rollover. - cosmos.base.v1beta1.Coin minimum_rollover_amount = 9 [(gogoproto.nullable) = false]; - // Number of seconds that a claim period lasts. - uint64 claim_period_seconds = 10; - // Time that a RewardProgram should start and switch to STARTED state. - google.protobuf.Timestamp program_start_time = 11 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // Time that a RewardProgram is expected to end, based on data when it was setup. - google.protobuf.Timestamp expected_program_end_time = 12 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // Time that a RewardProgram MUST end. - google.protobuf.Timestamp program_end_time_max = 13 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // Used internally to calculate and track the current claim period's ending time. - google.protobuf.Timestamp claim_period_end_time = 14 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // Time the RewardProgram switched to FINISHED state. Initially set as empty. - google.protobuf.Timestamp actual_program_end_time = 15 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // Number of claim periods this program will run for. - uint64 claim_periods = 16; - // Current claim period of the RewardProgram. Uses 1-based indexing. - uint64 current_claim_period = 17; - // maximum number of claim periods a reward program can rollover. - uint64 max_rollover_claim_periods = 18; - // Current state of the RewardProgram. - State state = 19; - // Grace period after a RewardProgram FINISHED. It is the number of seconds until a RewardProgram enters the EXPIRED - // state. - uint64 expiration_offset = 20; - // Actions that count towards the reward. - repeated QualifyingAction qualifying_actions = 21 [(gogoproto.nullable) = false]; -} - -// ClaimPeriodRewardDistribution, this is updated at the end of every claim period. -message ClaimPeriodRewardDistribution { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // The claim period id. - uint64 claim_period_id = 1; - // The id of the reward program that this reward belongs to. - uint64 reward_program_id = 2; - // The sum of all the granted rewards for this claim period. - cosmos.base.v1beta1.Coin total_rewards_pool_for_claim_period = 3 [(gogoproto.nullable) = false]; - // The final allocated rewards for this claim period. - cosmos.base.v1beta1.Coin rewards_pool = 4 [(gogoproto.nullable) = false]; - // The total number of granted shares for this claim period. - int64 total_shares = 5; - // A flag representing if the claim period for this reward has ended. - bool claim_period_ended = 6; -} - -// RewardAccountState contains state at the claim period level for a specific address. -message RewardAccountState { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - // ClaimStatus is the state a claim is in - enum ClaimStatus { - // undefined state - CLAIM_STATUS_UNSPECIFIED = 0; - // unclaimable status - CLAIM_STATUS_UNCLAIMABLE = 1; - // unclaimable claimable - CLAIM_STATUS_CLAIMABLE = 2; - // unclaimable claimed - CLAIM_STATUS_CLAIMED = 3; - // unclaimable expired - CLAIM_STATUS_EXPIRED = 4; - } - // The id of the reward program that this share belongs to. - uint64 reward_program_id = 1; - // The id of the claim period that the share belongs to. - uint64 claim_period_id = 2; - // Owner of the reward account state. - string address = 3; - // The number of actions performed by this account, mapped by action type. - repeated ActionCounter action_counter = 4; - // The amount of granted shares for the address in the reward program's claim period. - uint64 shares_earned = 5; - // The status of the claim. - ClaimStatus claim_status = 6; -} - -// QualifyingAction can be one of many action types. -message QualifyingAction { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - // type of action to process - oneof type { - ActionDelegate delegate = 1; - ActionTransfer transfer = 2; - ActionVote vote = 3; - } -} - -// QualifyingActions contains a list of QualifyingActions. -message QualifyingActions { - // The actions that count towards the reward. - repeated QualifyingAction qualifying_actions = 1 [(gogoproto.nullable) = false]; -} - -// ActionDelegate represents the delegate action and its required eligibility criteria. -message ActionDelegate { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // Minimum number of successful delegates. - uint64 minimum_actions = 1; - // Maximum number of successful delegates. - uint64 maximum_actions = 2; - // Minimum amount that the user must have currently delegated on the validator. - cosmos.base.v1beta1.Coin minimum_delegation_amount = 3; - // Maximum amount that the user must have currently delegated on the validator. - cosmos.base.v1beta1.Coin maximum_delegation_amount = 4; - // Minimum percentile that can be below the validator's power ranking. - string minimum_active_stake_percentile = 5 [ - (cosmos_proto.scalar) = "cosmos.Dec", - (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", - (gogoproto.nullable) = false - ]; - // Maximum percentile that can be below the validator's power ranking. - string maximum_active_stake_percentile = 6 [ - (cosmos_proto.scalar) = "cosmos.Dec", - (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", - (gogoproto.nullable) = false - ]; -} - -// ActionTransfer represents the transfer action and its required eligibility criteria. -message ActionTransfer { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // Minimum number of successful transfers. - uint64 minimum_actions = 1; - // Maximum number of successful transfers. - uint64 maximum_actions = 2; - // Minimum delegation amount the account must have across all validators, for the transfer action to be counted. - cosmos.base.v1beta1.Coin minimum_delegation_amount = 3 [(gogoproto.nullable) = false]; -} - -// ActionVote represents the voting action and its required eligibility criteria. -message ActionVote { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // Minimum number of successful votes. - uint64 minimum_actions = 1; - // Maximum number of successful votes. - uint64 maximum_actions = 2; - // Minimum delegation amount the account must have across all validators, for the vote action to be counted. - cosmos.base.v1beta1.Coin minimum_delegation_amount = 3 [(gogoproto.nullable) = false]; - // Positive multiplier that is applied to the shares awarded by the vote action when conditions - // are met(for now the only condition is the current vote is a validator vote). A value of zero will behave the same - // as one - uint64 validator_multiplier = 4; -} - -// ActionCounter is a key-value pair that maps action type to the number of times it was performed. -message ActionCounter { - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // The type of action performed. - string action_type = 1; - // The number of times this action has been performed - uint64 number_of_actions = 2; -} \ No newline at end of file diff --git a/proto/provenance/reward/v1/tx.proto b/proto/provenance/reward/v1/tx.proto deleted file mode 100644 index 9664c799b4..0000000000 --- a/proto/provenance/reward/v1/tx.proto +++ /dev/null @@ -1,143 +0,0 @@ -syntax = "proto3"; -package provenance.reward.v1; - -import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; -import "cosmos/base/v1beta1/coin.proto"; -import "provenance/reward/v1/reward.proto"; -import "cosmos/msg/v1/msg.proto"; - -option go_package = "github.com/provenance-io/provenance/x/reward/types"; -option java_package = "io.provenance.reward.v1"; -option java_multiple_files = true; - -// Msg -service Msg { - - // CreateRewardProgram is the RPC endpoint for creating a rewards program - rpc CreateRewardProgram(MsgCreateRewardProgramRequest) returns (MsgCreateRewardProgramResponse); - - // EndRewardProgram is the RPC endpoint for ending a rewards program - rpc EndRewardProgram(MsgEndRewardProgramRequest) returns (MsgEndRewardProgramResponse); - - // ClaimRewards is the RPC endpoint for claiming rewards belonging to completed claim periods of a reward program - rpc ClaimRewards(MsgClaimRewardsRequest) returns (MsgClaimRewardsResponse); - - // ClaimAllRewards is the RPC endpoint for claiming rewards for completed claim periods of every reward program for - // the signer of the tx. - rpc ClaimAllRewards(MsgClaimAllRewardsRequest) returns (MsgClaimAllRewardsResponse); -} - -// MsgCreateRewardProgramRequest is the request type for creating a reward program RPC -message MsgCreateRewardProgramRequest { - option (cosmos.msg.v1.signer) = "distribute_from_address"; - - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // title for the reward program. - string title = 1; - // description for the reward program. - string description = 2; - // provider address for the reward program funds and signer of message. - string distribute_from_address = 3; - // total reward pool for the reward program. - cosmos.base.v1beta1.Coin total_reward_pool = 4 [(gogoproto.nullable) = false]; - // maximum amount of funds an address can be rewarded per claim period. - cosmos.base.v1beta1.Coin max_reward_per_claim_address = 5 [(gogoproto.nullable) = false]; - // start time of the reward program. - google.protobuf.Timestamp program_start_time = 6 [ - (gogoproto.stdtime) = true, - (gogoproto.nullable) = false, - (gogoproto.jsontag) = "program_start_time,omitempty", - (gogoproto.moretags) = "yaml:\"program_start_time,omitempty\"" - ]; - // number of claim periods the reward program runs for. - uint64 claim_periods = 7; - // number of days a claim period will exist. - uint64 claim_period_days = 8; - // maximum number of claim periods a reward program can rollover. - uint64 max_rollover_claim_periods = 9; - // number of days before a reward program will expire after it has ended. - uint64 expire_days = 10; - // actions that count towards the reward. - repeated QualifyingAction qualifying_actions = 11 [(gogoproto.nullable) = false]; -} - -// MsgCreateRewardProgramResponse is the response type for creating a reward program RPC -message MsgCreateRewardProgramResponse { - // reward program id that is generated on creation. - uint64 id = 1; -} - -// MsgEndRewardProgramRequest is the request type for ending a reward program RPC -message MsgEndRewardProgramRequest { - option (cosmos.msg.v1.signer) = "program_owner_address"; - - option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = true; - - // reward program id to end. - uint64 reward_program_id = 1; - // owner of the reward program that funds were distributed from. - string program_owner_address = 2; -} - -// MsgEndRewardProgramResponse is the response type for ending a reward program RPC -message MsgEndRewardProgramResponse {} - -// MsgClaimRewardsRequest is the request type for claiming reward from reward program RPC -message MsgClaimRewardsRequest { - option (cosmos.msg.v1.signer) = "reward_address"; - - // reward program id to claim rewards. - uint64 reward_program_id = 1; - // reward address and signer of msg to send claimed rewards to. - string reward_address = 2; -} - -// MsgClaimRewardsResponse is the response type for claiming reward from reward program RPC -message MsgClaimRewardsResponse { - option (gogoproto.equal) = true; - // details about acquired rewards from reward program. - RewardProgramClaimDetail claim_details = 1 [(gogoproto.nullable) = false]; -} - -// MsgClaimRewardsResponse is the request type for claiming rewards from all reward programs RPC -message MsgClaimAllRewardsRequest { - option (cosmos.msg.v1.signer) = "reward_address"; - - // reward address and signer of msg to send claimed rewards to. - string reward_address = 1; -} - -// MsgClaimRewardsResponse is the response type for claiming rewards from all reward programs RPC -message MsgClaimAllRewardsResponse { - option (gogoproto.equal) = true; - // total rewards claimed for all eligible claim periods in all programs. - repeated cosmos.base.v1beta1.Coin total_reward_claim = 1 [(gogoproto.nullable) = false]; - // details about acquired rewards from a reward program. - repeated RewardProgramClaimDetail claim_details = 2; -} - -// ClaimedRewardPeriodDetail is information regarding an addresses' shares and reward for a claim period. -message ClaimedRewardPeriodDetail { - option (gogoproto.equal) = true; - // claim period id - uint64 claim_period_id = 1; - // total shares accumulated for claim period - uint64 total_shares = 2; - // total rewards for claim period - cosmos.base.v1beta1.Coin claim_period_reward = 3 [(gogoproto.nullable) = false]; -} - -// RewardProgramClaimDetail is the response object regarding an address's shares and reward for a reward program. -message RewardProgramClaimDetail { - option (gogoproto.equal) = true; - // reward program id. - uint64 reward_program_id = 1; - // total rewards claimed for all eligible claim periods in program. - cosmos.base.v1beta1.Coin total_reward_claim = 2 [(gogoproto.nullable) = false]; - // claim period details. - repeated ClaimedRewardPeriodDetail claimed_reward_period_details = 3; -} \ No newline at end of file diff --git a/third_party/proto/cosmos/authz/v1beta1/authz.proto b/third_party/proto/cosmos/authz/v1beta1/authz.proto index 3fee736436..f10e819e4b 100644 --- a/third_party/proto/cosmos/authz/v1beta1/authz.proto +++ b/third_party/proto/cosmos/authz/v1beta1/authz.proto @@ -21,6 +21,19 @@ message GenericAuthorization { string msg = 1; } +// CountAuthorization gives the grantee unrestricted permissions to execute +// the provided method on behalf of the granter's account up to the allowed authorizations count. +message CountAuthorization { + option (amino.name) = "cosmos-sdk/CountAuthorization"; + option (cosmos_proto.implements_interface) = "cosmos.authz.v1beta1.Authorization"; + + // Msg, identified by it's type URL, to grant unrestricted permissions to execute + string msg = 1; + + // Allowed number of authorizations assigned to grantee + int32 allowed_authorizations = 2; +} + // Grant gives permissions to execute // the provide method with expiration time. message Grant { diff --git a/third_party/proto/cosmos/bank/v1beta1/tx.proto b/third_party/proto/cosmos/bank/v1beta1/tx.proto index a4e8fae41f..49660b7fa7 100644 --- a/third_party/proto/cosmos/bank/v1beta1/tx.proto +++ b/third_party/proto/cosmos/bank/v1beta1/tx.proto @@ -33,6 +33,9 @@ service Msg { // // Since: cosmos-sdk 0.47 rpc SetSendEnabled(MsgSetSendEnabled) returns (MsgSetSendEnabledResponse); + + // UpdateDenomMetadata defines a method for updating the denom metadata. Only usable in x/gov proposal. + rpc UpdateDenomMetadata(MsgUpdateDenomMetadata) returns (MsgUpdateDenomMetadataResponse); } // MsgSend represents a message to send coins from one account to another. @@ -122,3 +125,19 @@ message MsgSetSendEnabled { // // Since: cosmos-sdk 0.47 message MsgSetSendEnabledResponse {} + +// MsgUpdateDenomMetadata defines the Msg/UpdateDenomMetadata request type. +message MsgUpdateDenomMetadata { + option (cosmos.msg.v1.signer) = "from_address"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string from_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string title = 2; + string description = 3; + Metadata metadata = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; +} + +// MsgUpdateDenomMetadataResponse defines the Msg/UpdateDenomMetadata response type. +message MsgUpdateDenomMetadataResponse {} \ No newline at end of file diff --git a/x/marker/keeper/keeper_test.go b/x/marker/keeper/keeper_test.go index 59dd7ff480..48f5e513ce 100644 --- a/x/marker/keeper/keeper_test.go +++ b/x/marker/keeper/keeper_test.go @@ -34,7 +34,6 @@ import ( "github.com/provenance-io/provenance/x/exchange" markerkeeper "github.com/provenance-io/provenance/x/marker/keeper" "github.com/provenance-io/provenance/x/marker/types" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" ) func TestAccountMapperGetSet(t *testing.T) { @@ -2886,7 +2885,6 @@ func TestReqAttrBypassAddrs(t *testing.T) { // Tests both GetReqAttrBypassAddrs and IsReqAttrBypassAddr. expectedNames := []string{ authtypes.FeeCollectorName, - rewardtypes.ModuleName, // quarantine.ModuleName, // TODO[1760]: quarantine govtypes.ModuleName, distrtypes.ModuleName, diff --git a/x/reward/abci.go b/x/reward/abci.go deleted file mode 100644 index 0cc956dcb1..0000000000 --- a/x/reward/abci.go +++ /dev/null @@ -1,27 +0,0 @@ -package reward - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/keeper" -) - -// BeginBlocker processes rewards module updates -func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { - defer func() { - if r := recover(); r != nil { - ctx.Logger().Error("reward BeginBlocker recovered from panic:", "r", r) - } - }() - k.UpdateUnexpiredRewardsProgram(ctx) -} - -// EndBlocker processes events for reward programs -func EndBlocker(ctx sdk.Context, k keeper.Keeper) { - defer func() { - if r := recover(); r != nil { - ctx.Logger().Error("reward EndBlocker recovered from panic:", "r", r) - } - }() - k.ProcessTransactions(ctx) -} diff --git a/x/reward/abci_test.go b/x/reward/abci_test.go deleted file mode 100644 index aa71190fca..0000000000 --- a/x/reward/abci_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package reward_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - - simapp "github.com/provenance-io/provenance/app" - "github.com/provenance-io/provenance/x/reward" -) - -func TestEndBlockWithNoActiveRewards(t *testing.T) { - var app *simapp.App - var ctx sdk.Context - - now := time.Now() - - app = simapp.Setup(t) - ctx = app.BaseApp.NewContext(false) - ctx = ctx.WithBlockHeight(1).WithBlockTime(now) - - ctx = ctx.WithBlockHeight(2) - require.NotPanics(t, func() { - reward.EndBlocker(ctx, app.RewardKeeper) - }) -} - -func TestBeginBlockWithNoActiveRewards(t *testing.T) { - var app *simapp.App - var ctx sdk.Context - - now := time.Now() - - app = simapp.Setup(t) - ctx = app.BaseApp.NewContext(false) - ctx = ctx.WithBlockHeight(1).WithBlockTime(now) - - ctx = ctx.WithBlockHeight(2) - require.NotPanics(t, func() { - reward.BeginBlocker(ctx, app.RewardKeeper) - }) -} diff --git a/x/reward/client/cli/cli_test.go b/x/reward/client/cli/cli_test.go deleted file mode 100644 index 06447885d1..0000000000 --- a/x/reward/client/cli/cli_test.go +++ /dev/null @@ -1,937 +0,0 @@ -package cli_test - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/suite" - - cmtcli "github.com/cometbft/cometbft/libs/cli" - - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - - "github.com/provenance-io/provenance/internal/antewrapper" - "github.com/provenance-io/provenance/internal/pioconfig" - "github.com/provenance-io/provenance/testutil" - rewardcli "github.com/provenance-io/provenance/x/reward/client/cli" - "github.com/provenance-io/provenance/x/reward/types" - rewardtypes "github.com/provenance-io/provenance/x/reward/types" -) - -type IntegrationTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network - keyring keyring.Keyring - keyringDir string - - accountAddr sdk.AccAddress - accountKey *secp256k1.PrivKey - accountAddresses []sdk.AccAddress - - activeRewardProgram types.RewardProgram - pendingRewardProgram types.RewardProgram - finishedRewardProgram types.RewardProgram - expiredRewardProgram types.RewardProgram - qualifyingActions []types.QualifyingAction -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - pioconfig.SetProvenanceConfig("", 0) - s.accountKey = secp256k1.GenPrivKeyFromSecret([]byte("acc2")) - addr, err := sdk.AccAddressFromHexUnsafe(s.accountKey.PubKey().Address().String()) - s.Require().NoError(err) - s.accountAddr = addr - - s.cfg = testutil.DefaultTestNetworkConfig() - genesisState := s.cfg.GenesisState - - s.cfg.NumValidators = 1 - s.GenerateAccountsWithKeyrings(2) - - var genBalances []banktypes.Balance - for i := range s.accountAddresses { - genBalances = append(genBalances, banktypes.Balance{Address: s.accountAddresses[i].String(), Coins: sdk.NewCoins( - sdk.NewInt64Coin("nhash", 100_000_000), sdk.NewInt64Coin(s.cfg.BondDenom, 100_000_000), - ).Sort()}) - } - genBalances = append(genBalances, banktypes.Balance{Address: "cosmos1w6t0l7z0yerj49ehnqwqaayxqpe3u7e23edgma", Coins: sdk.NewCoins( - sdk.NewInt64Coin("nhash", 100_000_000), sdk.NewInt64Coin(s.cfg.BondDenom, 100_000_000)).Sort()}) - var bankGenState banktypes.GenesisState - bankGenState.Params = banktypes.DefaultParams() - bankGenState.Balances = genBalances - bankDataBz, err := s.cfg.Codec.MarshalJSON(&bankGenState) - s.Require().NoError(err) - genesisState[banktypes.ModuleName] = bankDataBz - - var authData authtypes.GenesisState - var genAccounts []authtypes.GenesisAccount - authData.Params = authtypes.DefaultParams() - genAccounts = append(genAccounts, authtypes.NewBaseAccount(s.accountAddresses[0], nil, 3, 0)) - genAccounts = append(genAccounts, authtypes.NewBaseAccount(s.accountAddresses[1], nil, 4, 0)) - accounts, err := authtypes.PackAccounts(genAccounts) - s.Require().NoError(err) - authData.Accounts = accounts - authDataBz, err := s.cfg.Codec.MarshalJSON(&authData) - s.Require().NoError(err) - genesisState[authtypes.ModuleName] = authDataBz - - now := time.Now().UTC() - minimumDelegation := sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, 0) - maximumDelegation := sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, 10) - s.qualifyingActions = []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - } - - s.activeRewardProgram = types.NewRewardProgram( - "active title", - "active description", - 1, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - now, - 60*60, - 3, - 0, - 0, - s.qualifyingActions, - ) - s.activeRewardProgram.State = types.RewardProgram_STATE_STARTED - - s.finishedRewardProgram = types.NewRewardProgram( - "finished title", - "finished description", - 2, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - now.Add(-60*60*time.Second), - 60*60, - 3, - 0, - 60*60*24, - s.qualifyingActions, - ) - s.finishedRewardProgram.ActualProgramEndTime = now - s.finishedRewardProgram.State = types.RewardProgram_STATE_FINISHED - - s.pendingRewardProgram = types.NewRewardProgram( - "pending title", - "pending description", - 3, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - now.Add(60*60*time.Second), - 60*60, - 3, - 0, - 0, - s.qualifyingActions, - ) - s.pendingRewardProgram.State = types.RewardProgram_STATE_PENDING - - s.expiredRewardProgram = types.NewRewardProgram( - "expired title", - "expired description", - 4, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - now.Add(-60*60*time.Second), - 60*60, - 3, - 0, - 0, - s.qualifyingActions, - ) - s.expiredRewardProgram.State = types.RewardProgram_STATE_EXPIRED - - claimPeriodRewardDistributions := make([]rewardtypes.ClaimPeriodRewardDistribution, 101) - for i := 0; i < 101; i++ { - claimPeriodRewardDistributions[i] = rewardtypes.NewClaimPeriodRewardDistribution(uint64(i+1), 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 10), int64(i), false) - } - - rewardAccountState := make([]rewardtypes.RewardAccountState, 101) - for i := 0; i < 101; i++ { - rewardAccountState[i] = rewardtypes.NewRewardAccountState(1, uint64(i+1), s.accountAddr.String(), 10, []*types.ActionCounter{}) - switch i % 4 { - case 0: - rewardAccountState[i].ClaimStatus = rewardtypes.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE - case 1: - rewardAccountState[i].ClaimStatus = rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMABLE - case 2: - rewardAccountState[i].ClaimStatus = rewardtypes.RewardAccountState_CLAIM_STATUS_CLAIMED - case 3: - rewardAccountState[i].ClaimStatus = rewardtypes.RewardAccountState_CLAIM_STATUS_EXPIRED - } - } - - rewardData := rewardtypes.NewGenesisState( - uint64(5), - []rewardtypes.RewardProgram{ - s.activeRewardProgram, s.pendingRewardProgram, s.finishedRewardProgram, s.expiredRewardProgram, - }, - claimPeriodRewardDistributions, - rewardAccountState, - ) - - rewardDataBz, err := s.cfg.Codec.MarshalJSON(rewardData) - s.Require().NoError(err) - genesisState[rewardtypes.ModuleName] = rewardDataBz - - s.cfg.GenesisState = genesisState - - s.cfg.ChainID = antewrapper.SimAppChainID - s.cfg.TimeoutCommit = 500 * time.Millisecond - - s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) - s.Require().NoError(err, "network.New") - - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err, "WaitForHeight") -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.Require().NoError(s.network.WaitForNextBlock()) - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - -func (s *IntegrationTestSuite) GenerateAccountsWithKeyrings(number int) { - path := hd.CreateHDPath(118, 0, 0).String() - s.keyringDir = s.T().TempDir() - kr, err := keyring.New(s.T().Name(), "test", s.keyringDir, nil, s.cfg.Codec) - s.Require().NoError(err) - s.keyring = kr - for i := 0; i < number; i++ { - keyId := fmt.Sprintf("test_key%v", i) - info, _, err := kr.NewMnemonic(keyId, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - addr, err := info.GetAddress() - if err != nil { - panic(err) - } - s.accountAddresses = append(s.accountAddresses, addr) - } -} - -func (s *IntegrationTestSuite) TestQueryRewardPrograms() { - testCases := []struct { - name string - queryTypeArg string - byId bool - expectErrMsg string - expectedCode uint32 - expectedIds []uint64 - }{ - {"query all reward programs", - "all", - false, - "", - 0, - []uint64{1, 2, 3, 4, 5}, - }, - {"query active reward programs", - "active", - false, - "", - 0, - []uint64{1}, - }, - {"query pending reward programs", - "pending", - false, - "", - 0, - []uint64{3, 5}, - }, - {"query completed reward programs", - "completed", - false, - "", - 0, - []uint64{2, 4}, - }, - {"query outstanding reward programs", - "outstanding", - false, - "", - 0, - []uint64{1, 3, 5}, - }, - {"query by id reward programs", - "2", - true, - "", - 0, - []uint64{2}, - }, - {"query by id reward programs", - "99", - true, - "failed to query reward program 99: rpc error: code = Unknown desc = rpc error: code = Internal desc = unable to query for reward program by ID: reward program not found: unknown request", - 0, - []uint64{2}, - }, - {"query invalid query type", - "invalid", - true, - "invalid argument arg : invalid", - 0, - []uint64{}, - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetRewardProgramCmd(), []string{tc.queryTypeArg, fmt.Sprintf("--%s=json", cmtcli.OutputFlag)}) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else if tc.byId { - var response types.QueryRewardProgramByIDResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - s.Assert().Equal(tc.expectedIds[0], response.RewardProgram.Id) - } else { - var response types.QueryRewardProgramsResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - var rewardProgramIds []uint64 - for _, rp := range response.RewardPrograms { - rewardProgramIds = append(rewardProgramIds, rp.Id) - } - s.Assert().ElementsMatch(tc.expectedIds, rewardProgramIds, "should have all expected reward program ids") - } - }) - } -} - -func (s *IntegrationTestSuite) TestQueryClaimPeriodRewardDistributionAll() { - defaultMaxQueryIds := make([]uint64, 100) - for i := 0; i < 100; i++ { - defaultMaxQueryIds[i] = uint64(i + 1) - } - testCases := []struct { - name string - args []string - byId bool - expectErrMsg string - expectedCode uint32 - expectedIds []uint64 - }{ - {"query all reward programs get default page size of 100", - []string{ - "all", - }, - false, - "", - 0, - defaultMaxQueryIds, - }, - {"query all reward programs max out to default page size", - []string{ - "all", - "--limit", - "100", - }, - false, - "", - 0, - defaultMaxQueryIds, - }, - {"query all reward programs first page with 5 results", - []string{ - "all", - "--limit", - "5", - }, - false, - "", - 0, - []uint64{1, 2, 3, 4, 5}, - }, - {"query all reward programs second page with 5 results", - []string{ - "all", - "--limit", - "5", - "--page", - "2", - }, - false, - "", - 0, - []uint64{6, 7, 8, 9, 10}, - }, - {"query by program id and claim period", - []string{ - "1", - "2", - }, - true, - "", - 0, - []uint64{1, 2}, - }, - { - "query without claim period", - []string{ - "1", - }, - true, - "a reward_program_id and an claim_period_id are required", - 0, - []uint64{}, - }, - { - "query with invalid reward program id format", - []string{ - "1", - "a", - }, - true, - "strconv.Atoi: parsing \"a\": invalid syntax", - 0, - []uint64{}, - }, - { - "query with invalid reward program id format", - []string{ - "a", - "1", - }, - true, - "strconv.Atoi: parsing \"a\": invalid syntax", - 0, - []uint64{}, - }, - { - "query with invalid reward program id format", - []string{ - "100", - "100", - }, - true, - "reward does not exist for reward-id: 100 claim-id 100", - 0, - []uint64{}, - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetClaimPeriodRewardDistributionCmd(), append(tc.args, []string{fmt.Sprintf("--%s=json", cmtcli.OutputFlag)}...)) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else if tc.byId { - var response types.QueryClaimPeriodRewardDistributionsByIDResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - s.Assert().Equal(tc.expectedIds[0], response.ClaimPeriodRewardDistribution.RewardProgramId) - s.Assert().Equal(tc.expectedIds[1], response.ClaimPeriodRewardDistribution.ClaimPeriodId) - } else { - var response types.QueryClaimPeriodRewardDistributionsResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - var claimPeriodRewardDistIds []uint64 - for _, cprd := range response.ClaimPeriodRewardDistributions { - claimPeriodRewardDistIds = append(claimPeriodRewardDistIds, cprd.ClaimPeriodId) - } - s.Assert().ElementsMatch(tc.expectedIds, claimPeriodRewardDistIds, "should have all expected claim period ids") - } - }) - } -} - -func (s *IntegrationTestSuite) TestGetCmdRewardProgramAdd() { - actions := "{\"qualifying_actions\":[{\"delegate\":{\"minimum_actions\":\"0\",\"maximum_actions\":\"1\",\"minimum_delegation_amount\":{\"denom\":\"nhash\",\"amount\":\"0\"},\"maximum_delegation_amount\":{\"denom\":\"nhash\",\"amount\":\"100\"},\"minimum_active_stake_percentile\":\"0.000000000000000000\",\"maximum_active_stake_percentile\":\"1.000000000000000000\"}}]}" - soon := time.Now().Add(time.Hour * 24) - - testCases := []struct { - name string - args []string - expectErrMsg string - expectedCode uint32 - }{ - {"add reward program tx - valid", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52364", - "--claim-period-days=7", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "", - 0, - }, - {"add reward program tx - invalid total-reward-pool", - []string{ - "test add reward program", - "description", - "--total-reward-pool=invalid", - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "invalid decimal coin expression: invalid", - 0, - }, - {"add reward program tx - invalid max-reward-by-address", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - "--max-reward-by-address=invalid", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "invalid decimal coin expression: invalid", - 0, - }, - {"add reward program tx - invalid claim period days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=-1", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "invalid argument \"-1\" for \"--claim-period-days\" flag: strconv.ParseUint: parsing \"-1\": invalid syntax", - 0, - }, - {"add reward program tx - invalid expire days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=-1", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "invalid argument \"-1\" for \"--expire-days\" flag: strconv.ParseUint: parsing \"-1\": invalid syntax", - 0, - }, - {"add reward program tx - invalid reward period days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=-52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=1", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "invalid argument \"-52\" for \"--claim-periods\" flag: strconv.ParseUint: parsing \"-52\": invalid syntax", - 0, - }, - {"add reward program tx - invalid start time", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - "--start-time=invalid", - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", actions), - }, - "unable to parse time (invalid) required format is RFC3339 (2006-01-02T15:04:05Z07:00) , parsing time \"invalid\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"invalid\" as \"2006\"", - 0, - }, - {"add reward program tx - invalid ec", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", "actions"), - }, - "invalid character 'a' looking for beginning of value", - 0, - }, - {"add reward program tx - invalid type for claim periods", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=abc", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", "actions"), - }, - "invalid argument \"abc\" for \"--claim-periods\" flag: strconv.ParseUint: parsing \"abc\": invalid syntax", - 0, - }, - {"add reward program tx - invalid type for claim period days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=abc", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - fmt.Sprintf("--qualifying-actions=%s", "actions"), - }, - "invalid argument \"abc\" for \"--claim-period-days\" flag: strconv.ParseUint: parsing \"abc\": invalid syntax", - 0, - }, - {"add reward program tx - invalid type for claim period days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=abc", - fmt.Sprintf("--qualifying-actions=%s", "actions"), - }, - "invalid argument \"abc\" for \"--expire-days\" flag: strconv.ParseUint: parsing \"abc\": invalid syntax", - 0, - }, - {"add reward program tx - invalid type for claim period days", - []string{ - "test add reward program", - "description", - fmt.Sprintf("--total-reward-pool=580%s", s.cfg.BondDenom), - fmt.Sprintf("--max-reward-by-address=100%s", s.cfg.BondDenom), - "--claim-periods=52", - "--claim-period-days=10", - fmt.Sprintf("--start-time=%s", soon.Format(time.RFC3339)), - "--expire-days=14", - "--max-rollover-periods=abc", - fmt.Sprintf("--qualifying-actions=%s", "actions"), - }, - "invalid argument \"abc\" for \"--max-rollover-periods\" flag: strconv.ParseUint: parsing \"abc\": invalid syntax", - 0, - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, s.network.Validators[0].Address.String()), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // TODO[1760]: broadcast - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 10)).String()), - } - tc.args = append(tc.args, args...) - - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetCmdRewardProgramAdd(), append(tc.args, []string{fmt.Sprintf("--%s=json", cmtcli.OutputFlag)}...)) - var response sdk.TxResponse - marshalErr := clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else { - s.Assert().NoError(err) - s.Assert().NoError(marshalErr, out.String()) - } - }) - } -} - -func (s *IntegrationTestSuite) TestTxClaimReward() { - testCases := []struct { - name string - claimRewardArg string - expectErrMsg string - expectedCode uint32 - }{ - {"claim rewards tx - valid", - "1", - "", - 0, - }, - {"claim rewards tx - all", - "all", - "", - 0, - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, s.network.Validators[0].Address.String()), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // TODO[1760]: broadcast - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 10)).String()), - } - args = append(args, tc.claimRewardArg) - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetCmdClaimReward(), args) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else { - var response sdk.TxResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - marshalErr := clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(marshalErr) - s.Assert().Equal(tc.expectedCode, response.Code) - } - }) - } -} - -func (s *IntegrationTestSuite) TestTxEndRewardProgram() { - testCases := []struct { - name string - endRewardProgramId string - expectErrMsg string - expectedCode uint32 - signer string - }{ - { - name: "end reward program - valid", - endRewardProgramId: "1", - expectErrMsg: "", - expectedCode: 0, - signer: s.accountAddresses[0].String(), - }, - { - name: "end reward program - invalid id", - endRewardProgramId: "999", - expectErrMsg: "", - expectedCode: 3, - signer: s.accountAddresses[0].String(), - }, - { - name: "end reward program - invalid state", - endRewardProgramId: "2", - expectErrMsg: "", - expectedCode: 5, - signer: s.accountAddresses[0].String(), - }, - { - name: "end reward program - not authorized", - endRewardProgramId: "1", - expectErrMsg: "", - expectedCode: 4, - signer: s.accountAddresses[1].String(), - }, - { - name: "end reward program - invalid id format", - endRewardProgramId: "abc", - expectErrMsg: "invalid argument : abc", - expectedCode: 0, - signer: s.accountAddresses[0].String(), - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx.WithKeyringDir(s.keyringDir).WithKeyring(s.keyring) - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, tc.signer), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // TODO[1760]: broadcast - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 10)).String()), - } - args = append(args, tc.endRewardProgramId) - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetCmdEndRewardProgram(), args) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else { - var response sdk.TxResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - marshalErr := clientCtx.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(marshalErr) - s.Assert().Equal(tc.expectedCode, response.Code) - } - }) - } -} - -func (s *IntegrationTestSuite) TestQueryAllRewardsPerAddress() { - testCases := []struct { - name string - addressArg string - stateArg string - byId bool - expectErrMsg string - expectedCode uint32 - expectedIds []uint64 - expectedLength int64 - }{ - { - name: "query all reward by address", - addressArg: s.accountAddr.String(), - stateArg: "all", - byId: false, - expectErrMsg: "", - expectedCode: 0, - expectedIds: []uint64{1, 2, 3, 4}, - expectedLength: 100, - }, - { - name: "query unclaimable reward by address", - addressArg: s.accountAddr.String(), - stateArg: "unclaimable", - byId: false, - expectErrMsg: "", - expectedCode: 0, - expectedIds: []uint64{1, 5}, - expectedLength: 26, - }, - { - name: "query claimable reward by address", - addressArg: s.accountAddr.String(), - stateArg: "claimable", - byId: false, - expectErrMsg: "", - expectedCode: 0, - expectedIds: []uint64{2, 6}, - expectedLength: 25, - }, - { - name: "query claimed reward by address", - addressArg: s.accountAddr.String(), - stateArg: "claimed", - byId: false, - expectErrMsg: "", - expectedCode: 0, - expectedIds: []uint64{3, 7}, - expectedLength: 25, - }, - { - name: "query expired reward by address", - addressArg: s.accountAddr.String(), - stateArg: "expired", - byId: false, - expectErrMsg: "", - expectedCode: 0, - expectedIds: []uint64{4, 8}, - expectedLength: 25, - }, - { - name: "query reward by address", - addressArg: s.accountAddr.String(), - stateArg: "invalid", - byId: false, - expectErrMsg: "failed to query reward distributions. invalid is not a valid query param", - expectedCode: 0, - expectedIds: []uint64{}, - expectedLength: 0, - }, - { - name: "query reward by invalid address", - addressArg: "invalid address", - stateArg: "expired", - byId: false, - expectErrMsg: "failed to query reward distributions: rpc error: code = Unknown desc = decoding bech32 failed: invalid character in string: ' ': invalid address: unknown request", - expectedCode: 0, - expectedIds: []uint64{}, - expectedLength: 0, - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := s.network.Validators[0].ClientCtx - args := []string{tc.addressArg, tc.stateArg, fmt.Sprintf("--%s=json", cmtcli.OutputFlag)} - out, err := clitestutil.ExecTestCLICmd(clientCtx, rewardcli.GetRewardsByAddressCmd(), args) - if len(tc.expectErrMsg) > 0 { - s.Assert().EqualError(err, tc.expectErrMsg) - } else if tc.byId { - var response types.QueryRewardDistributionsByAddressResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - } else { - var response types.QueryRewardDistributionsByAddressResponse - s.Assert().NoError(err) - err = s.cfg.Codec.UnmarshalJSON(out.Bytes(), &response) - s.Assert().NoError(err) - var actualClaimIds []uint64 - for _, ras := range response.RewardAccountState { - if ras.RewardProgramId == 1 { - actualClaimIds = append(actualClaimIds, ras.ClaimId) - } - } - for _, eId := range tc.expectedIds { - s.Assert().Contains(actualClaimIds, eId, fmt.Sprintf("missing claim id %d for reward id 1", eId)) - } - s.Assert().Equal(int(tc.expectedLength), len(response.RewardAccountState), "length of results does not match") - } - }) - } -} diff --git a/x/reward/client/cli/query.go b/x/reward/client/cli/query.go deleted file mode 100644 index 76ec2212ae..0000000000 --- a/x/reward/client/cli/query.go +++ /dev/null @@ -1,265 +0,0 @@ -package cli - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/version" - - "github.com/provenance-io/provenance/x/reward/types" -) - -var cmdStart = fmt.Sprintf("%s query reward", version.AppName) - -func GetQueryCmd() *cobra.Command { - queryCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Querying commands for the rewards module", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - queryCmd.AddCommand( - GetRewardProgramCmd(), - GetClaimPeriodRewardDistributionCmd(), - GetRewardsByAddressCmd(), - ) - return queryCmd -} - -func GetRewardProgramCmd() *cobra.Command { - const all = "all" - const pending = "pending" - const active = "active" - const completed = "completed" - const outstanding = "outstanding" - cmd := &cobra.Command{ - Use: "reward-program {reward_program_id|\"all\"|\"pending\"|\"active\"\"completed\"|\"outstanding\"}", - Aliases: []string{"rp", "rewardprogram"}, - Short: "Query the current reward programs", - Long: fmt.Sprintf(`%[1]s reward-program {reward_program_id} - gets the reward program for a given id. -%[1]s reward-program all - gets all the reward programs -%[1]s reward-program active - gets all the active reward programs`, cmdStart), - Args: cobra.ExactArgs(1), - Example: fmt.Sprintf(`%[1]s reward-program 1 -%[1]s reward-program all -%[1]s reward-program pending -%[1]s reward-program active -%[1]s reward-program outstanding -%[1]s reward-program completed`, cmdStart), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - queryClient := types.NewQueryClient(clientCtx) - - var request types.QueryRewardProgramsRequest - arg0 := strings.TrimSpace(args[0]) - switch arg0 { - case all: - request.QueryType = types.QueryRewardProgramsRequest_QUERY_TYPE_ALL - case pending: - request.QueryType = types.QueryRewardProgramsRequest_QUERY_TYPE_PENDING - case active: - request.QueryType = types.QueryRewardProgramsRequest_QUERY_TYPE_ACTIVE - case completed: - request.QueryType = types.QueryRewardProgramsRequest_QUERY_TYPE_FINISHED - case outstanding: - request.QueryType = types.QueryRewardProgramsRequest_QUERY_TYPE_OUTSTANDING - default: - return outputRewardProgramByID(clientCtx, queryClient, arg0) - } - - var response *types.QueryRewardProgramsResponse - if response, err = queryClient.RewardPrograms( - context.Background(), - &request, - ); err != nil { - return fmt.Errorf("failed to query reward programs: %w", err) - } - - return clientCtx.PrintProto(response) - }, - } - flags.AddQueryFlagsToCmd(cmd) - flags.AddPaginationFlagsToCmd(cmd, "all") - return cmd -} - -func outputRewardProgramByID(client client.Context, queryClient types.QueryClient, arg string) error { - programID, err := strconv.Atoi(arg) - if err != nil { - return fmt.Errorf("invalid argument arg : %s", arg) - } - - var response *types.QueryRewardProgramByIDResponse - if response, err = queryClient.RewardProgramByID( - context.Background(), - &types.QueryRewardProgramByIDRequest{Id: uint64(programID)}, - ); err != nil { - return fmt.Errorf("failed to query reward program %d: %w", programID, err) - } - - if response.GetRewardProgram() == nil { - return fmt.Errorf("reward program %d does not exist", programID) - } - - return client.PrintProto(response) -} - -func GetClaimPeriodRewardDistributionCmd() *cobra.Command { - const all = "all" - - cmd := &cobra.Command{ - Use: "claim-period-reward-distribution {\"all\"} | {reward_program_id} {claim_period_id}", - Aliases: []string{"cprd", "reward-distribution", "rd", "claim-periods"}, - Short: "Query the current claim period reward distributions", - Long: fmt.Sprintf(`%[1]s claim-period-reward-distribution {reward_program_id} {claim_period_id} - gets the reward program for the given reward_program_id and claim_period_id -%[1]s reward-distribution all - gets all the claim period reward distributions`, cmdStart), - Args: cobra.RangeArgs(1, 2), - Example: fmt.Sprintf(`%[1]s claim-period-reward-distribution 1 1 -%[1]s reward-distribution all`, cmdStart), - RunE: func(cmd *cobra.Command, args []string) error { - arg0 := strings.TrimSpace(args[0]) - if arg0 == all { - return outputClaimPeriodRewardDistributionAll(cmd) - } - - if len(args) != 2 { - return fmt.Errorf("a reward_program_id and an claim_period_id are required") - } - arg1 := args[1] - - return outputClaimPeriodRewardDistributionByID(cmd, arg0, arg1) - }, - } - flags.AddPaginationFlagsToCmd(cmd, "all") - flags.AddQueryFlagsToCmd(cmd) - return cmd -} - -func GetRewardsByAddressCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "reward-by-address {address} {\"all\"|\"unclaimable\"|\"claimable\"|\"claimed\"|\"expired\"}", - Aliases: []string{"rpa", "reward-per-address"}, - Short: "Query all the reward distributions for an address", - Long: fmt.Sprintf(`%[1]s reward-by-address {address} {query-type} - gets the reward amount for the given address based on the filter values`, cmdStart), - Args: cobra.ExactArgs(2), - Example: fmt.Sprintf(`%[1]s reward-by-address pb1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk all -%[1]s reward-by-address pb1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk unclaimable -%[1]s reward-by-address pb1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk claimable -%[1]s reward-by-address pb1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk claimed -%[1]s reward-by-address pb1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk expired`, cmdStart), - RunE: func(cmd *cobra.Command, args []string) error { - arg0 := strings.TrimSpace(args[0]) - arg1 := args[1] - - return queryRewardDistributionByAddress(cmd, arg0, arg1) - }, - } - flags.AddQueryFlagsToCmd(cmd) - return cmd -} - -// Query for all ClaimPeriodRewardDistributions -func outputClaimPeriodRewardDistributionAll(cmd *cobra.Command) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - pageReq, err := client.ReadPageRequest(cmd.Flags()) // TODO[1760]: cli: ReadPageRequestWithPageKeyDecoded - if err != nil { - return err - } - - queryClient := types.NewQueryClient(clientCtx) - - var response *types.QueryClaimPeriodRewardDistributionsResponse - if response, err = queryClient.ClaimPeriodRewardDistributions( - context.Background(), - &types.QueryClaimPeriodRewardDistributionsRequest{Pagination: pageReq}, - ); err != nil { - return fmt.Errorf("failed to query reward programs: %w", err) - } - - return clientCtx.PrintProto(response) -} - -// Query for a ClaimPeriodRewardDistribution by Id -func outputClaimPeriodRewardDistributionByID(cmd *cobra.Command, rewardID, claimPeriodID string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - queryClient := types.NewQueryClient(clientCtx) - id, err := strconv.Atoi(rewardID) - if err != nil { - return err - } - claimID, err := strconv.Atoi(claimPeriodID) - if err != nil { - return err - } - var response *types.QueryClaimPeriodRewardDistributionsByIDResponse - if response, err = queryClient.ClaimPeriodRewardDistributionsByID( - context.Background(), - &types.QueryClaimPeriodRewardDistributionsByIDRequest{ - RewardId: uint64(id), - ClaimPeriodId: uint64(claimID), - }, - ); err != nil { - return fmt.Errorf("failed to query reward claim %d: %w", id, err) - } - - if response.GetClaimPeriodRewardDistribution() == nil { - return fmt.Errorf("reward does not exist for reward-id: %s claim-id %s", rewardID, claimPeriodID) - } - - return clientCtx.PrintProto(response) -} - -// Query for RewardAccountByAddress depending on claim status -func queryRewardDistributionByAddress(cmd *cobra.Command, address string, queryType string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - - var claimStatus types.RewardAccountState_ClaimStatus - switch queryType { - case "all": - claimStatus = types.RewardAccountState_CLAIM_STATUS_UNSPECIFIED - case "unclaimable": - claimStatus = types.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE - case "claimable": - claimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - case "claimed": - claimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMED - case "expired": - claimStatus = types.RewardAccountState_CLAIM_STATUS_EXPIRED - default: - return fmt.Errorf("failed to query reward distributions. %s is not a valid query param", queryType) - } - - queryClient := types.NewQueryClient(clientCtx) - - var response *types.QueryRewardDistributionsByAddressResponse - if response, err = queryClient.RewardDistributionsByAddress( - context.Background(), - &types.QueryRewardDistributionsByAddressRequest{ - Address: address, - ClaimStatus: claimStatus, - }, - ); err != nil { - return fmt.Errorf("failed to query reward distributions: %w", err) - } - - return clientCtx.PrintProto(response) -} diff --git a/x/reward/client/cli/tx.go b/x/reward/client/cli/tx.go deleted file mode 100644 index f40406677d..0000000000 --- a/x/reward/client/cli/tx.go +++ /dev/null @@ -1,225 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/spf13/cobra" - - "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/cosmos/cosmos-sdk/version" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// Flag names and values -const ( - FlagTotalRewardPool = "total-reward-pool" - FlagMaxRewardByAddress = "max-reward-by-address" - FlagStartTime = "start-time" - FlagClaimPeriods = "claim-periods" - FlagClaimPeriodDays = "claim-period-days" - FlagExpireDays = "expire-days" - FlagQualifyingActions = "qualifying-actions" - FlagMaxRolloverClaimPeriods = "max-rollover-periods" -) - -func NewTxCmd() *cobra.Command { - txCmd := &cobra.Command{ - Use: types.ModuleName, - Aliases: []string{"r"}, - Short: "Transaction commands for the reward module", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - txCmd.AddCommand( - GetCmdRewardProgramAdd(), - GetCmdEndRewardProgram(), - GetCmdClaimReward(), - ) - - return txCmd -} - -func GetCmdRewardProgramAdd() *cobra.Command { - cmd := &cobra.Command{ - Use: "add-reward-program [title] [description]", - Args: cobra.ExactArgs(2), - Aliases: []string{"arp"}, - Short: "Add a reward program", - Long: strings.TrimSpace(`Add a reward program`), - Example: fmt.Sprintf(`$ %[1]s tx reward add-reward-program "Program Title" "A short description" \ - --total-reward-pool 580nhash \ - --max-reward-by-address 10nhash \ - --start-time '2050-01-15T00:00:00Z' \ - --claim-periods 52 \ - --max-rollover-periods 4 \ - --claim-period-days 7 \ - --expire-days 14 \ - --qualifying-actions '{"qualifying_actions":[{"delegate":{"minimum_actions":"0","maximum_actions":"1","minimum_delegation_amount":{"denom":"nhash","amount":"0"},"maximum_delegation_amount":{"denom":"nhash","amount":"100"},"minimum_active_stake_percentile":"0.000000000000000000","maximum_active_stake_percentile":"1.000000000000000000"}}]}' - `, version.AppName), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - coinStr, err := cmd.Flags().GetString(FlagTotalRewardPool) - if err != nil { - return err - } - coin, err := sdk.ParseCoinNormalized(coinStr) - if err != nil { - return err - } - maxCoinStr, err := cmd.Flags().GetString(FlagMaxRewardByAddress) - if err != nil { - return err - } - maxCoin, err := sdk.ParseCoinNormalized(maxCoinStr) - if err != nil { - return err - } - startTimeStr, err := cmd.Flags().GetString(FlagStartTime) - if err != nil || len(startTimeStr) == 0 { - return err - } - startTime, err := time.Parse(time.RFC3339, startTimeStr) - if err != nil { - return fmt.Errorf("unable to parse time (%v) required format is RFC3339 (%v) , %w", startTimeStr, time.RFC3339, err) - } - rewardProgramDays, err := cmd.Flags().GetUint64(FlagClaimPeriods) - if err != nil { - return err - } - claimPeriodDays, err := cmd.Flags().GetUint64(FlagClaimPeriodDays) - if err != nil { - return err - } - expireDays, err := cmd.Flags().GetUint64(FlagExpireDays) - if err != nil { - return err - } - contents, err := cmd.Flags().GetString(FlagQualifyingActions) - if err != nil { - return err - } - maxRolloverPeriods, err := cmd.Flags().GetUint64(FlagMaxRolloverClaimPeriods) - if err != nil { - return err - } - var actions types.QualifyingActions - err = clientCtx.Codec.UnmarshalJSON([]byte(contents), &actions) - if err != nil { - return err - } - callerAddr := clientCtx.GetFromAddress() - msg := types.NewMsgCreateRewardProgramRequest( - args[0], - args[1], - callerAddr.String(), - coin, - maxCoin, - startTime, - rewardProgramDays, - claimPeriodDays, - maxRolloverPeriods, - expireDays, - actions.QualifyingActions, - ) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - flags.AddTxFlagsToCmd(cmd) - cmd.Flags().String(FlagTotalRewardPool, "", "coins for reward program") - cmd.Flags().String(FlagMaxRewardByAddress, "", "max amount of coins a single address can claim in rewards") - cmd.Flags().String(FlagStartTime, "", "time to start the rewards program, this must be a time in the future or within the first epoch of format YYYY-MM-DDTHH:MM:SSZ00:00 (2012-11-01T22:08:41+07:00)") - cmd.Flags().Uint64(FlagClaimPeriods, 52, "number of claim periods the reward program runs") - cmd.Flags().Uint64(FlagClaimPeriodDays, 7, "number of days for a claim period interval") - cmd.Flags().Uint64(FlagExpireDays, 7, "number of days to expire program after it has ended") - cmd.Flags().String(FlagQualifyingActions, "", "json representation of qualifying actions") - cmd.Flags().Uint64(FlagMaxRolloverClaimPeriods, 0, "max number of rollover claim periods") - return cmd -} - -func GetCmdEndRewardProgram() *cobra.Command { - cmd := &cobra.Command{ - Use: "end-reward-program [reward-program-id]", - Args: cobra.ExactArgs(1), - Aliases: []string{"erp", "end", "er"}, - Short: "End a reward program", - Long: strings.TrimSpace(`End a reward program. This will trigger the reward program to end after the current claim period`), - Example: fmt.Sprintf(`$ %[1]s tx reward end-reward-program 1`, version.AppName), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - callerAddr := clientCtx.GetFromAddress() - programID, err := strconv.Atoi(args[0]) - if err != nil { - return fmt.Errorf("invalid argument : %s", args[0]) - } - - msg := types.NewMsgEndRewardProgramRequest( - uint64(programID), - callerAddr.String(), - ) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - flags.AddTxFlagsToCmd(cmd) - return cmd -} - -func GetCmdClaimReward() *cobra.Command { - const all = "all" - cmd := &cobra.Command{ - Use: "claim-reward [reward-program-id|all]", - Args: cobra.ExactArgs(1), - Aliases: []string{"cr", "claim"}, - Short: "Claim reward for specified reward program or all programs", - Long: strings.TrimSpace(`Claim reward for a specified reward program or all programs. This will transfer all unclaimed rewards for outstanding claim periods to signers address.`), - Example: fmt.Sprintf(`$ %[1]s tx reward claim-reward 1 --from mykey -$ %[1]s tx reward claim-reward all --from mykey`, version.AppName), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - arg0 := strings.TrimSpace(args[0]) - if arg0 != all { - return claimRewardProgramByID(clientCtx, arg0, cmd) - } - - callerAddr := clientCtx.GetFromAddress() - msg := types.NewMsgClaimAllRewardsRequest( - callerAddr.String(), - ) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - flags.AddTxFlagsToCmd(cmd) - return cmd -} - -func claimRewardProgramByID(client client.Context, arg string, cmd *cobra.Command) error { - programID, err := strconv.Atoi(arg) - if err != nil { - return fmt.Errorf("invalid argument arg : %s", arg) - } - callerAddr := client.GetFromAddress() - - msg := types.NewMsgClaimRewardsRequest( - uint64(programID), - callerAddr.String(), - ) - return tx.GenerateOrBroadcastTxCLI(client, cmd.Flags(), msg) -} diff --git a/x/reward/handler.go b/x/reward/handler.go deleted file mode 100644 index 592198df94..0000000000 --- a/x/reward/handler.go +++ /dev/null @@ -1,36 +0,0 @@ -package reward - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/provenance-io/provenance/x/reward/keeper" - "github.com/provenance-io/provenance/x/reward/types" -) - -// NewHandler returns a handler for reward messages. -// TODO[1760]: reward: Delete the reward NewHandler. -func NewHandler(k keeper.Keeper) func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgCreateRewardProgramRequest: - res, err := msgServer.CreateRewardProgram(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - case *types.MsgEndRewardProgramRequest: - res, err := msgServer.EndRewardProgram(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - case *types.MsgClaimRewardsRequest: - res, err := msgServer.ClaimRewards(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - case *types.MsgClaimAllRewardsRequest: - res, err := msgServer.ClaimAllRewards(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - default: - return nil, sdkerrors.ErrUnknownRequest.Wrapf("unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/reward/keeper/claim_reward_distribution.go b/x/reward/keeper/claim_reward_distribution.go deleted file mode 100644 index cfd88596e5..0000000000 --- a/x/reward/keeper/claim_reward_distribution.go +++ /dev/null @@ -1,70 +0,0 @@ -package keeper - -import ( - storetypes "cosmossdk.io/store/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// SetClaimPeriodRewardDistribution sets the ClaimPeriodRewardDistribution in the keeper -func (k Keeper) SetClaimPeriodRewardDistribution(ctx sdk.Context, claimPeriodRewardDistribution types.ClaimPeriodRewardDistribution) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshal(&claimPeriodRewardDistribution) - store.Set(types.GetClaimPeriodRewardDistributionKey(claimPeriodRewardDistribution.ClaimPeriodId, claimPeriodRewardDistribution.RewardProgramId), bz) -} - -// GetClaimPeriodRewardDistribution returns a ClaimPeriodRewardDistribution by epoch id and reward id -func (k Keeper) GetClaimPeriodRewardDistribution(ctx sdk.Context, claimPeriodID uint64, rewardID uint64) (claimPeriodRewardDistribution types.ClaimPeriodRewardDistribution, err error) { - store := ctx.KVStore(k.storeKey) - key := types.GetClaimPeriodRewardDistributionKey(claimPeriodID, rewardID) - bz := store.Get(key) - if len(bz) == 0 { - return claimPeriodRewardDistribution, nil - } - err = k.cdc.Unmarshal(bz, &claimPeriodRewardDistribution) - return claimPeriodRewardDistribution, err -} - -// IterateClaimPeriodRewardDistributions iterates all epoch reward distributions with the given handler function. -func (k Keeper) IterateClaimPeriodRewardDistributions(ctx sdk.Context, handle func(ClaimPeriodRewardDistribution types.ClaimPeriodRewardDistribution) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.ClaimPeriodRewardDistributionKeyPrefix) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - record := types.ClaimPeriodRewardDistribution{} - if err := k.cdc.Unmarshal(iterator.Value(), &record); err != nil { - return err - } - if handle(record) { - break - } - } - return nil -} - -// GetAllClaimPeriodRewardDistributions Gets all the Epoch Reward Distributions -func (k Keeper) GetAllClaimPeriodRewardDistributions(sdkCtx sdk.Context) ([]types.ClaimPeriodRewardDistribution, error) { - var rewardDistributions []types.ClaimPeriodRewardDistribution - err := k.IterateClaimPeriodRewardDistributions(sdkCtx, func(rewardDistribution types.ClaimPeriodRewardDistribution) (stop bool) { - rewardDistributions = append(rewardDistributions, rewardDistribution) - return false - }) - if err != nil { - return nil, err - } - return rewardDistributions, nil -} - -// RemoveClaimPeriodRewardDistribution Removes an ClaimPeriodRewardDistribution -func (k Keeper) RemoveClaimPeriodRewardDistribution(ctx sdk.Context, claimPeriodID uint64, rewardID uint64) bool { - store := ctx.KVStore(k.storeKey) - key := types.GetClaimPeriodRewardDistributionKey(claimPeriodID, rewardID) - keyExists := store.Has(key) - if keyExists { - store.Delete(key) - } - return keyExists -} diff --git a/x/reward/keeper/claim_reward_distribution_test.go b/x/reward/keeper/claim_reward_distribution_test.go deleted file mode 100644 index 969bf1dbd2..0000000000 --- a/x/reward/keeper/claim_reward_distribution_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package keeper_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/assert" - - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestGetSetClaimPeriodRewardDistribution() { - rewardDistribution := types.NewClaimPeriodRewardDistribution( - 1, - 2, - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 200000), - 3, - true, - ) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, rewardDistribution) - retrieved, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 2) - s.Assert().NoError(err) - s.Assert().Equal(rewardDistribution.ClaimPeriodId, retrieved.ClaimPeriodId, "claim period id must match") - s.Assert().Equal(rewardDistribution.RewardProgramId, retrieved.RewardProgramId, "reward program id must match") - s.Assert().Equal(rewardDistribution.RewardsPool, retrieved.RewardsPool, "rewards pool must match") - s.Assert().Equal(rewardDistribution.TotalRewardsPoolForClaimPeriod, retrieved.TotalRewardsPoolForClaimPeriod, "total rewards pool must match") - s.Assert().Equal(rewardDistribution.TotalShares, retrieved.TotalShares, "total shares must match") - s.Assert().Equal(rewardDistribution.ClaimPeriodEnded, retrieved.ClaimPeriodEnded, "claim period ended must match") - - retrieved, err = s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 99) - s.Assert().NoError(err) - s.Assert().Equal(uint64(0), retrieved.RewardProgramId, "reward program id must be invalid") -} - -func (s *KeeperTestSuite) TestIterateClaimPeriodRewardDistributions() { - tests := []struct { - name string - distributions []types.ClaimPeriodRewardDistribution - halt bool - counter int - }{ - { - "valid - can handle empty distributions", - []types.ClaimPeriodRewardDistribution{}, - false, - 0, - }, - { - "valid - can handle one distribution", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - false, - 1, - }, - { - "valid - can handle multiple distributions", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 2, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - false, - 3, - }, - { - "valid - can handle halting", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 2, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - true, - 1, - }, - } - - for _, tc := range tests { - s.T().Run(tc.name, func(t *testing.T) { - counter := 0 - for _, distribution := range tc.distributions { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - err := s.app.RewardKeeper.IterateClaimPeriodRewardDistributions(s.ctx, func(ClaimPeriodRewardDistribution types.ClaimPeriodRewardDistribution) (stop bool) { - counter += 1 - return tc.halt - }) - assert.NoError(t, err, "No error is thrown") - assert.Equal(t, tc.counter, counter, "iterated the correct number of times") - }) - } -} - -func (s *KeeperTestSuite) TestGetAllClaimPeriodRewardDistributions() { - tests := []struct { - name string - distributions []types.ClaimPeriodRewardDistribution - expected int - }{ - { - "valid - can handle empty distributions", - []types.ClaimPeriodRewardDistribution{}, - 0, - }, - { - "valid - can handle one distribution", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - 1, - }, - { - "valid - can handle multiple distributions", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 2, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(1, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - 3, - }, - } - - for _, tc := range tests { - s.T().Run(tc.name, func(t *testing.T) { - for _, distribution := range tc.distributions { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - results, err := s.app.RewardKeeper.GetAllClaimPeriodRewardDistributions(s.ctx) - assert.NoError(t, err, "No error is thrown") - assert.Equal(t, tc.expected, len(results), "returned the correct number of claim period reward distributions") - }) - } -} - -func (s *KeeperTestSuite) TestRemoveClaimPeriodRewardDistribution() { - tests := []struct { - name string - distributions []types.ClaimPeriodRewardDistribution - expected int - removed bool - }{ - { - "valid - can handle removal of invalid claim period", - []types.ClaimPeriodRewardDistribution{}, - 0, - false, - }, - { - "valid - can handle valid removal", - []types.ClaimPeriodRewardDistribution{ - types.NewClaimPeriodRewardDistribution(1, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - types.NewClaimPeriodRewardDistribution(2, 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false), - }, - 1, - true, - }, - } - - for _, tc := range tests { - s.T().Run(tc.name, func(t *testing.T) { - for _, distribution := range tc.distributions { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - removed := s.app.RewardKeeper.RemoveClaimPeriodRewardDistribution(s.ctx, 1, 1) - results, err := s.app.RewardKeeper.GetAllClaimPeriodRewardDistributions(s.ctx) - assert.Equal(t, tc.removed, removed, "removal status does not match expectation") - assert.NoError(t, err, "No error is thrown") - assert.Equal(t, tc.expected, len(results), "returned the correct number of claim period reward distributions") - }) - } -} diff --git a/x/reward/keeper/genesis.go b/x/reward/keeper/genesis.go deleted file mode 100644 index ca902b0de0..0000000000 --- a/x/reward/keeper/genesis.go +++ /dev/null @@ -1,72 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// ExportGenesis returns a GenesisState for a given context. -func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { - rewardProgramID, err := k.GetRewardProgramID(ctx) - if err != nil { - panic(err) - } - - rewardPrograms := make([]types.RewardProgram, 0) - rewardProgramRecords := func(rewardProgram types.RewardProgram) (bool, error) { - rewardPrograms = append(rewardPrograms, rewardProgram) - return false, nil - } - if err := k.IterateRewardPrograms(ctx, rewardProgramRecords); err != nil { - panic(err) - } - - claimPeriodRewardDistributions := make([]types.ClaimPeriodRewardDistribution, 0) - claimPeriodRewardDistributionRecords := func(ClaimPeriodRewardDistribution types.ClaimPeriodRewardDistribution) bool { - claimPeriodRewardDistributions = append(claimPeriodRewardDistributions, ClaimPeriodRewardDistribution) - return false - } - if err := k.IterateClaimPeriodRewardDistributions(ctx, claimPeriodRewardDistributionRecords); err != nil { - panic(err) - } - - rewardAccountStates := make([]types.RewardAccountState, 0) - rewardAccountStateRecords := func(RewardAccountState types.RewardAccountState) bool { - rewardAccountStates = append(rewardAccountStates, RewardAccountState) - return false - } - - for _, claim := range claimPeriodRewardDistributions { - if err := k.IterateRewardAccountStates(ctx, claim.RewardProgramId, claim.ClaimPeriodId, rewardAccountStateRecords); err != nil { - panic(err) - } - } - - return types.NewGenesisState( - rewardProgramID, - rewardPrograms, - claimPeriodRewardDistributions, - rewardAccountStates, - ) -} - -// InitGenesis new reward genesis -func (k Keeper) InitGenesis(ctx sdk.Context, data *types.GenesisState) { - if err := data.Validate(); err != nil { - panic(err) - } - k.setRewardProgramID(ctx, data.RewardProgramId) - - for _, rewardProgram := range data.RewardPrograms { - k.SetRewardProgram(ctx, rewardProgram) - } - - for _, ClaimPeriodRewardDistributions := range data.ClaimPeriodRewardDistributions { - k.SetClaimPeriodRewardDistribution(ctx, ClaimPeriodRewardDistributions) - } - - for _, RewardAccountStates := range data.RewardAccountStates { - k.SetRewardAccountState(ctx, RewardAccountStates) - } -} diff --git a/x/reward/keeper/keeper.go b/x/reward/keeper/keeper.go deleted file mode 100644 index 6f77f57b4b..0000000000 --- a/x/reward/keeper/keeper.go +++ /dev/null @@ -1,40 +0,0 @@ -package keeper - -import ( - "github.com/cosmos/cosmos-sdk/codec" - storetypes "cosmossdk.io/store/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - - "github.com/provenance-io/provenance/x/reward/types" -) - -const StoreKey = types.ModuleName - -type Keeper struct { - storeKey storetypes.StoreKey - cdc codec.BinaryCodec - stakingKeeper types.StakingKeeper - govKeeper *govkeeper.Keeper - bankKeeper bankkeeper.Keeper - authkeeper authkeeper.AccountKeeper -} - -func NewKeeper( - cdc codec.BinaryCodec, - key storetypes.StoreKey, - stakingKeeper types.StakingKeeper, - govKeeper *govkeeper.Keeper, - bankKeeper bankkeeper.Keeper, - authKeeper authkeeper.AccountKeeper, -) Keeper { - return Keeper{ - storeKey: key, - cdc: cdc, - stakingKeeper: stakingKeeper, - govKeeper: govKeeper, - bankKeeper: bankKeeper, - authkeeper: authKeeper, - } -} diff --git a/x/reward/keeper/keeper_test.go b/x/reward/keeper/keeper_test.go deleted file mode 100644 index c45701612e..0000000000 --- a/x/reward/keeper/keeper_test.go +++ /dev/null @@ -1,806 +0,0 @@ -package keeper_test - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/suite" - - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - - "github.com/provenance-io/provenance/app" - simapp "github.com/provenance-io/provenance/app" - "github.com/provenance-io/provenance/x/reward" - "github.com/provenance-io/provenance/x/reward/types" -) - -var ( - PKs = simapp.CreateTestPubKeys(500) -) - -type KeeperTestSuite struct { - suite.Suite - - app *simapp.App - ctx sdk.Context - queryClient types.QueryClient - // TODO[1760]: reward: Get rid of this reward handler and call the desired keeper endpoints directly in these tests. - handler func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) - - accountAddr sdk.AccAddress - accountKey *secp256k1.PrivKey - keyring keyring.Keyring - keyringDir string - accountAddresses []sdk.AccAddress -} - -func (s *KeeperTestSuite) CreateAccounts(number int) { - for i := 0; i < number; i++ { - accountKey := secp256k1.GenPrivKeyFromSecret([]byte(fmt.Sprintf("acc%d", i+2))) - addr, err := sdk.AccAddressFromHexUnsafe(accountKey.PubKey().Address().String()) - s.Require().NoError(err) - s.accountAddr = addr - s.accountAddresses = append(s.accountAddresses, addr) - } -} - -func (s *KeeperTestSuite) SetupTest() { - s.app = app.Setup(s.T()) - s.CreateAccounts(4) - s.handler = reward.NewHandler(s.app.RewardKeeper) - s.ctx = s.app.BaseApp.NewContextLegacy(false, cmtproto.Header{Time: time.Now().UTC()}) - s.createTestValidators(10) - s.Require().NoError(testutil.FundModuleAccount(s.ctx, s.app.BankKeeper, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("nhash", 10000000000000))), "funding module") - - queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, s.app.RewardKeeper) - s.queryClient = types.NewQueryClient(queryHelper) -} - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(KeeperTestSuite)) -} - -// Test no reward programs. Nothing should happen -func (s *KeeperTestSuite) TestDelegateAgainstNoRewardPrograms() { - SetupEventHistoryWithDelegates(s) - - // Advance one day - s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + (24 * 60 * 60)) - s.Assert().NotPanics(func() { - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - }) - - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += 1 - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no shares should be created") -} - -// Test against inactive reward programs. They should not be updated -func (s *KeeperTestSuite) TestDelegateAgainstInactiveRewardPrograms() { - SetupEventHistoryWithDelegates(s) - - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no shares are granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += 1 - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no shares should be created") - - programs, err := s.app.RewardKeeper.GetAllRewardPrograms(s.ctx) - s.Assert().Equal(1, len(programs)) - s.Assert().NoError(err, "get all reward programs should not throw error") - - activePrograms, err := s.app.RewardKeeper.GetAllActiveRewardPrograms(s.ctx) - s.Assert().NoError(err, "get all active reward programs should not throw error") - s.Assert().Equal(0, len(activePrograms)) -} - -// Test against delegate reward program. No delegate happens. -func (s *KeeperTestSuite) TestNonDelegateAgainstRewardProgram() { - setupEventHistory(s) - - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_STARTED - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no shares are granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += 1 - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no shares should be created") - - programs, err := s.app.RewardKeeper.GetAllRewardPrograms(s.ctx) - s.Assert().Equal(1, len(programs)) - s.Assert().NoError(err, "get all reward programs should not throw error") - - activePrograms, err := s.app.RewardKeeper.GetAllActiveRewardPrograms(s.ctx) - s.Assert().NoError(err, "get all active reward programs should not throw error") - s.Assert().Equal(1, len(activePrograms)) -} - -func (s *KeeperTestSuite) createDelegateEvents(validator, amount, sender, shares string) sdk.Events { - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("module", "staking"), - sdk.NewAttribute("sender", sender), - } - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("validator", validator), - sdk.NewAttribute("amount", amount), - sdk.NewAttribute("new_shares", shares), - } - event1 := sdk.NewEvent("delegate", attributes1...) - event2 := sdk.NewEvent("message", attributes2...) - events := sdk.Events{ - event1, - event2, - } - return events -} - -func (s *KeeperTestSuite) createValidatorEvent(validator, amount, sender string) sdk.Events { - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("validator", validator), - sdk.NewAttribute("amount", amount), - } - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("module", "staking"), - sdk.NewAttribute("sender", sender), - } - event1 := sdk.NewEvent("create_validator", attributes1...) - event2 := sdk.NewEvent("message", attributes2...) - events := sdk.Events{ - event1, - event2, - } - return events -} - -// Test against delegate reward program. Grant 1 share -func (s *KeeperTestSuite) TestSingleDelegate() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure one share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(1, count, "1 share should be created") -} - -// Test against delegate reward program. Grant 2 share -func (s *KeeperTestSuite) TestMultipleDelegate() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure one share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(2, count, "2 shares should be created") -} - -// Test against delegate reward program. Not enough actions -func (s *KeeperTestSuite) TestDelegateBelowMinimumActions() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 10, - MaximumActions: 20, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(7, 1), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when below minimum actions") -} - -// Test against delegate reward program. Too many actions -func (s *KeeperTestSuite) TestDelegateAboveMaximumActions() { - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 0, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when above maximum actions") -} - -// Test against delegate reward program. Below delegation amount -func (s *KeeperTestSuite) TestDelegateBelowMinimumDelegation() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 100) - maximumDelegation := sdk.NewInt64Coin("nhash", 200) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when below minimum delegation amount") -} - -// Test against delegate reward program. Above delegation amount -func (s *KeeperTestSuite) TestDelegateAboveMaximumDelegation() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 50) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when above maximum delegation amount") -} - -// Test against delegate reward program. Below percentile -func (s *KeeperTestSuite) TestDelegateBelowMinimumPercentile() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(7, 1), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when below minimum delegation percentage") -} - -// Test against delegate reward program. Above percentile -func (s *KeeperTestSuite) TestDelegateAboveMaximumPercentile() { - // Create inactive reward program - action := types.NewActionDelegate() - action.MaximumActions = 10 - action.MinimumActions = 1 - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(0, 0) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - minimumDelegation := sdk.NewInt64Coin("nhash", 0) - maximumDelegation := sdk.NewInt64Coin("nhash", 100) - action.MinimumDelegationAmount = &minimumDelegation - action.MaximumDelegationAmount = &maximumDelegation - - coin := sdk.NewInt64Coin("hotdog", 10000) - maxCoin := sdk.NewInt64Coin("hotdog", 100) - - now := time.Now().UTC() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - coin, - maxCoin, - now, - 60*60, - 3, - 0, - 2, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(2, 1), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.StartRewardProgram(s.ctx, &rewardProgram), "StartRewardProgram") - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - // We want to set the events here - validators := getTestValidators(6, 6) - delegates := s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000") - delegates = delegates.AppendEvents(s.createDelegateEvents(validators[0].OperatorAddress, "1000000000nhash", "cosmos15ky9du8a2wlstz6fpx3p4mqpjyrm5cgxwpuzvh", "50000000000000.000000000000000000")) - SetupEventHistory(s, delegates) - reward.EndBlocker(s.ctx, s.app.RewardKeeper) - - // Ensure no share is granted - count := 0 - err := s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 { - count += int(state.GetSharesEarned()) - return true - } - return false - }) - s.Assert().NoError(err, "iterate should not throw error") - s.Assert().Equal(0, count, "no share should be created when above maximum delegation percentage") -} diff --git a/x/reward/keeper/msg_server.go b/x/reward/keeper/msg_server.go deleted file mode 100644 index 730217734d..0000000000 --- a/x/reward/keeper/msg_server.go +++ /dev/null @@ -1,148 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -type msgServer struct { - Keeper -} - -// NewMsgServerImpl returns an implementation of the account MsgServer interface -// for the provided Keeper. -func NewMsgServerImpl(keeper Keeper) types.MsgServer { - return &msgServer{Keeper: keeper} -} - -var _ types.MsgServer = msgServer{} - -// CreateRewardProgram creates new reward program from msg -func (s msgServer) CreateRewardProgram(goCtx context.Context, msg *types.MsgCreateRewardProgramRequest) (*types.MsgCreateRewardProgramResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - rewardProgramID, err := s.Keeper.GetNextRewardProgramID(ctx) - if err != nil { - return &types.MsgCreateRewardProgramResponse{}, err - } - - claimPeriodDaysInSeconds := uint64(types.DayInSeconds) * msg.GetClaimPeriodDays() - expirationOffsetInSeconds := uint64(types.DayInSeconds) * msg.GetExpireDays() - - rewardProgram := types.NewRewardProgram( - msg.Title, - msg.Description, - rewardProgramID, - msg.DistributeFromAddress, - msg.TotalRewardPool, - msg.MaxRewardPerClaimAddress, - msg.ProgramStartTime.UTC(), - claimPeriodDaysInSeconds, - msg.ClaimPeriods, - msg.MaxRolloverClaimPeriods, - expirationOffsetInSeconds, - msg.QualifyingActions, - ) - err = s.Keeper.CreateRewardProgram(ctx, rewardProgram) - if err != nil { - return &types.MsgCreateRewardProgramResponse{}, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewardProgramCreated, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", rewardProgramID)), - ), - ) - - return &types.MsgCreateRewardProgramResponse{Id: rewardProgramID}, nil -} - -// EndRewardProgram ends reward program from msg -func (s msgServer) EndRewardProgram(goCtx context.Context, msg *types.MsgEndRewardProgramRequest) (*types.MsgEndRewardProgramResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - rewardProgram, err := s.Keeper.GetRewardProgram(ctx, msg.RewardProgramId) - if err != nil { - return &types.MsgEndRewardProgramResponse{}, err - } - if rewardProgram.DistributeFromAddress != msg.ProgramOwnerAddress { - return &types.MsgEndRewardProgramResponse{}, types.ErrEndRewardProgramNotAuthorized - } - if rewardProgram.State != types.RewardProgram_STATE_PENDING && rewardProgram.State != types.RewardProgram_STATE_STARTED { - return &types.MsgEndRewardProgramResponse{}, types.ErrEndrewardProgramIncorrectState - } - - s.Keeper.EndingRewardProgram(ctx, rewardProgram) - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewardProgramEnded, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", rewardProgram.Id)), - ), - ) - - return &types.MsgEndRewardProgramResponse{}, nil -} - -// ClaimRewards claims specific rewards for a user. -func (s msgServer) ClaimRewards(goCtx context.Context, req *types.MsgClaimRewardsRequest) (*types.MsgClaimRewardsResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - details, reward, err := s.Keeper.ClaimRewards(ctx, req.GetRewardProgramId(), req.GetRewardAddress()) - if err != nil { - return nil, err - } - - if len(details) > 0 { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeClaimRewards, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", req.RewardProgramId)), - sdk.NewAttribute(types.AttributeKeyRewardsClaimAddress, req.GetRewardAddress()), - ), - ) - } - - return &types.MsgClaimRewardsResponse{ - ClaimDetails: types.RewardProgramClaimDetail{ - RewardProgramId: req.GetRewardProgramId(), - TotalRewardClaim: reward, - ClaimedRewardPeriodDetails: details, - }, - }, nil -} - -// ClaimAllRewards claims all rewards for a user. -func (s msgServer) ClaimAllRewards(goCtx context.Context, req *types.MsgClaimAllRewardsRequest) (*types.MsgClaimAllRewardsResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - details, reward, err := s.Keeper.ClaimAllRewards(ctx, req.GetRewardAddress()) - if err != nil { - return nil, err - } - - programIDs := make([]uint64, 0, len(details)) - for _, detail := range details { - programIDs = append(programIDs, detail.GetRewardProgramId()) - } - - if len(details) > 0 { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeClaimAllRewards, - sdk.NewAttribute(types.AttributeKeyRewardProgramIDs, fmt.Sprintf("%v", programIDs)), - sdk.NewAttribute(types.AttributeKeyRewardsClaimAddress, req.GetRewardAddress()), - ), - ) - } - - return &types.MsgClaimAllRewardsResponse{ - TotalRewardClaim: reward, - ClaimDetails: details, - }, nil -} diff --git a/x/reward/keeper/msg_server_test.go b/x/reward/keeper/msg_server_test.go deleted file mode 100644 index 8381f5b949..0000000000 --- a/x/reward/keeper/msg_server_test.go +++ /dev/null @@ -1,553 +0,0 @@ -package keeper_test - -import ( - "time" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestCreateRewardProgramTransaction() { - - minimumDelegation := sdk.NewInt64Coin("nhash", 100) - maximumDelegation := sdk.NewInt64Coin("nhash", 200) - s.Require().NoError(testutil.FundAccount(s.ctx, s.app.BankKeeper, s.accountAddresses[0], sdk.NewCoins(sdk.NewInt64Coin("nhash", 100000))), "funding account") - - msg := types.NewMsgCreateRewardProgramRequest( - "title", - "description", - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time.Now(), - 4, - 2, - 1, - 4, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().NoError(err, "msg server should handle a new valid reward program") - s.Assert().Less(0, len(result.GetEvents()), "should have emitted events") - s.Assert().Equal(result.Events[len(result.Events)-1].Type, "reward_program_created", "emitted event should have correct event type") - s.Assert().Equal(1, len(result.Events[len(result.Events)-1].Attributes), "emitted event should have correct event number of attributes") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[0].Key, "reward_program_id", "should have correct key") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[0].Value, "1", "should have correct value") - - program, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 1) - s.Assert().NoError(err, "No error should be returned") - s.Assert().Nil(program.Validate(), "should not have a validation error") -} - -func (s *KeeperTestSuite) TestCreateRewardProgramFailedTransaction() { - - minimumDelegation := sdk.NewInt64Coin("nhash", 100) - maximumDelegation := sdk.NewInt64Coin("nhash", 200) - - msg := types.NewMsgCreateRewardProgramRequest( - "title", - "description", - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time.Now(), - 4, - 2, - 1, - 4, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: &minimumDelegation, - MaximumDelegationAmount: &maximumDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().Error(err, "msg server should throw error for invalid creation") - s.Assert().Nil(result, "result should be nil and have no events") - - program, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 1) - s.Assert().Error(err, "error should be returned") - s.Assert().NotNil(program.Validate(), "should have validation issue for invalid program") -} - -func (s *KeeperTestSuite) TestRewardClaimTransaction() { - - now := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for i := 1; i <= int(rewardProgram.GetClaimPeriods()); i++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(i), s.accountAddresses[0].String(), 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(i), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - - msg := types.NewMsgClaimRewardsRequest(1, s.accountAddresses[0].String()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - - s.Assert().NoError(err, "msg server should handle valid reward claim") - s.Assert().NotNil(result, "msg server should emit events") - s.Assert().Less(0, len(result.GetEvents()), "should have emitted events") - s.Assert().Equal(result.Events[len(result.Events)-1].Type, "claim_rewards", "emitted event should have correct event type") - s.Assert().Equal(2, len(result.Events[len(result.Events)-1].Attributes), "emitted event should have correct number of attributes") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[0].Key, "reward_program_id", "should have correct key") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[0].Value, "1", "should have correct program id") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[1].Key, "rewards_claim_address", "should have correct key") - s.Assert().Equal(result.Events[len(result.Events)-1].Attributes[1].Value, s.accountAddresses[0].String(), "should have correct address value") -} - -func (s *KeeperTestSuite) TestRewardClaimInvalidTransaction() { - - msg := types.NewMsgClaimRewardsRequest(1, "invalid address") - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - - s.Assert().Error(err, "msg server should handle an invalid reward claim") - s.Assert().Nil(result, "should have no emitted events") -} - -func (s *KeeperTestSuite) TestRewardClaimTransactionInvalidClaimer() { - - now := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for i := 1; i <= int(rewardProgram.GetClaimPeriods()); i++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(i), s.accountAddresses[0].String(), 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(i), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - - msg := types.NewMsgClaimRewardsRequest(1, s.accountAddresses[1].String()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().NoError(err, "msg server should handle valid reward claim") - s.Assert().NotNil(result, "msg server should emit events") - - var response types.MsgClaimRewardsResponse - response.Unmarshal(result.Data) - s.Assert().Equal(uint64(1), response.GetClaimDetails().RewardProgramId, "should have correct reward program id") - s.Assert().Equal(0, len(response.GetClaimDetails().ClaimedRewardPeriodDetails), "should have no details") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), response.GetClaimDetails().TotalRewardClaim, "should have no reward claim") -} - -func (s *KeeperTestSuite) TestClaimAllRewardsTransaction() { - now := s.ctx.BlockTime() - - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for j := 1; j <= int(rewardProgram.GetClaimPeriods()); j++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(j), s.accountAddresses[0].String(), 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(j), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - } - - msg := types.NewMsgClaimAllRewardsRequest(s.accountAddresses[0].String()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().NoError(err, "msg server should handle valid reward claim") - s.Assert().NotNil(result, "msg server should emit events") - - var response types.MsgClaimAllRewardsResponse - response.Unmarshal(result.Data) - details := response.ClaimDetails - s.Assert().Equal(sdk.NewInt64Coin("nhash", 900), response.TotalRewardClaim[0], "should total up the rewards from the periods") - s.Assert().Equal(3, len(details), "should have every reward program") - for i := 0; i < len(details); i++ { - s.Assert().Equal(3, len(details[i].ClaimedRewardPeriodDetails), "should have claims from every period") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 300), details[i].TotalRewardClaim, "should total up the rewards from the periods") - s.Assert().Equal(uint64(i+1), details[i].RewardProgramId, "should have the correct id") - } -} - -func (s *KeeperTestSuite) TestClaimAllRewardsNoProgramsTransaction() { - msg := types.NewMsgClaimAllRewardsRequest(s.accountAddresses[0].String()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().NoError(err, "no error should be returned in a valid call") - - var response types.MsgClaimAllRewardsResponse - response.Unmarshal(result.Data) - details := response.ClaimDetails - - s.Assert().Equal(0, len(response.TotalRewardClaim), "should have no nhash") - s.Assert().Equal(0, len(details), "should have no reward program") -} - -func (s *KeeperTestSuite) TestRewardClaimAllRewardsInvalidAddressTransaction() { - now := s.ctx.BlockTime() - - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for j := 1; j <= int(rewardProgram.GetClaimPeriods()); j++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(j), s.accountAddresses[0].String(), 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(j), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - } - - msg := types.NewMsgClaimAllRewardsRequest("invalid address") - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().Error(err, "error should be returned else state store will commit") - s.Assert().Nil(result) -} - -func (s *KeeperTestSuite) TestClaimAllRewardsExpiredTransaction() { - now := s.ctx.BlockTime() - - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for j := 1; j <= int(rewardProgram.GetClaimPeriods()); j++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(j), s.accountAddresses[0].String(), 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_EXPIRED - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(j), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - } - - msg := types.NewMsgClaimAllRewardsRequest(s.accountAddresses[0].String()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - s.Assert().NoError(err, "no error should be returned in a valid call") - - var response types.MsgClaimAllRewardsResponse - response.Unmarshal(result.Data) - details := response.ClaimDetails - - s.Assert().Equal(0, len(response.TotalRewardClaim), "should have no nhash") - s.Assert().Equal(0, len(details), "should have no reward program") -} - -func (s *KeeperTestSuite) TestEndRewardProgramRequest() { - testCases := []struct { - name string - id uint64 - address string - expectErr bool - expectErrMsg string - }{ - {"end reward program request - invalid reward program id", - 88, - s.accountAddresses[0].String(), - true, - "reward program not found", - }, - {"end reward program request - invalid executor", - 1, - s.accountAddresses[1].String(), - true, - "not authorized to end the reward program", - }, - {"end reward program request - invalid state for reward program", - 3, - s.accountAddresses[0].String(), - true, - "unable to end a reward program that is finished or expired", - }, - {"end reward program request - valid request in pending state", - 1, - s.accountAddresses[0].String(), - false, - "", - }, - {"end reward program request - valid requested in started state", - 2, - s.accountAddresses[0].String(), - false, - "", - }, - } - - now := s.ctx.BlockTime() - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 3, - 0, - uint64(now.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }, - ) - switch i + 1 { - case 1: - rewardProgram.State = types.RewardProgram_STATE_PENDING - case 2: - rewardProgram.State = types.RewardProgram_STATE_STARTED - rewardProgram.CurrentClaimPeriod = 1 - case 3: - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - } - - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - msg := types.NewMsgEndRewardProgramRequest(tc.id, tc.address) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) - result, err := s.handler(s.ctx, msg) - if tc.expectErr { - s.Assert().Error(err) - s.Assert().Equal(tc.expectErrMsg, err.Error()) - } else { - s.Assert().NoError(err) - var response types.MsgEndRewardProgramResponse - err = response.Unmarshal(result.Data) - s.Assert().NoError(err) - } - }) - } - -} diff --git a/x/reward/keeper/query_server.go b/x/reward/keeper/query_server.go deleted file mode 100644 index 1c18ccb98f..0000000000 --- a/x/reward/keeper/query_server.go +++ /dev/null @@ -1,205 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "cosmossdk.io/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/query" - - "github.com/provenance-io/provenance/x/reward/types" -) - -var _ types.QueryServer = Keeper{} - -// RewardPrograms returns a list of reward programs matching the query type. -func (k Keeper) RewardPrograms(ctx context.Context, req *types.QueryRewardProgramsRequest) (*types.QueryRewardProgramsResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - sdkCtx := sdk.UnwrapSDKContext(ctx) - var err error - - rewardProgramStates := []types.RewardProgram_State{} - switch req.QueryType { - case types.QueryRewardProgramsRequest_QUERY_TYPE_PENDING: - rewardProgramStates = []types.RewardProgram_State{types.RewardProgram_STATE_PENDING} - case types.QueryRewardProgramsRequest_QUERY_TYPE_ACTIVE: - rewardProgramStates = []types.RewardProgram_State{types.RewardProgram_STATE_STARTED} - case types.QueryRewardProgramsRequest_QUERY_TYPE_FINISHED: - rewardProgramStates = []types.RewardProgram_State{types.RewardProgram_STATE_FINISHED, types.RewardProgram_STATE_EXPIRED} - case types.QueryRewardProgramsRequest_QUERY_TYPE_OUTSTANDING: - rewardProgramStates = []types.RewardProgram_State{types.RewardProgram_STATE_PENDING, types.RewardProgram_STATE_STARTED} - } - - response := types.QueryRewardProgramsResponse{} - kvStore := sdkCtx.KVStore(k.storeKey) - prefixStore := prefix.NewStore(kvStore, types.RewardProgramKeyPrefix) - pageResponse, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - var rewardProgram types.RewardProgram - vErr := rewardProgram.Unmarshal(value) - - if vErr != nil { - return false, vErr - } - - matched := rewardProgram.MatchesState(rewardProgramStates) - if accumulate && matched { - response.RewardPrograms = append(response.RewardPrograms, rewardProgram) - } - - return matched, nil - }) - - if err != nil { - return nil, status.Errorf(codes.Internal, fmt.Sprintf("unable to query all reward programs: %v", err)) - } - response.Pagination = pageResponse - return &response, nil -} - -// RewardProgramByID returns a reward program matching the ID. -func (k Keeper) RewardProgramByID(ctx context.Context, req *types.QueryRewardProgramByIDRequest) (*types.QueryRewardProgramByIDResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - sdkCtx := sdk.UnwrapSDKContext(ctx) - rewardProgram, err := k.GetRewardProgram(sdkCtx, req.GetId()) - if err != nil { - return &types.QueryRewardProgramByIDResponse{}, status.Errorf(codes.Internal, fmt.Sprintf("unable to query for reward program by ID: %v", err)) - } - return &types.QueryRewardProgramByIDResponse{RewardProgram: &rewardProgram}, nil -} - -// ClaimPeriodRewardDistributions returns a list of claim period reward distributions matching the claim_status. -func (k Keeper) ClaimPeriodRewardDistributions(ctx context.Context, req *types.QueryClaimPeriodRewardDistributionsRequest) (*types.QueryClaimPeriodRewardDistributionsResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - response := types.QueryClaimPeriodRewardDistributionsResponse{} - sdkCtx := sdk.UnwrapSDKContext(ctx) - kvStore := sdkCtx.KVStore(k.storeKey) - prefixStore := prefix.NewStore(kvStore, types.ClaimPeriodRewardDistributionKeyPrefix) - pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - var claimPeriodRewardDist types.ClaimPeriodRewardDistribution - vErr := claimPeriodRewardDist.Unmarshal(value) - - if vErr != nil { - return false, vErr - } - - if accumulate { - response.ClaimPeriodRewardDistributions = append(response.ClaimPeriodRewardDistributions, claimPeriodRewardDist) - } - - return true, nil - }) - if err != nil { - return &response, status.Error(codes.Unavailable, err.Error()) - } - response.Pagination = pageRes - return &response, nil -} - -// ClaimPeriodRewardDistributionsByID returns a claim period reward distribution matching the ID. -func (k Keeper) ClaimPeriodRewardDistributionsByID(ctx context.Context, req *types.QueryClaimPeriodRewardDistributionsByIDRequest) (*types.QueryClaimPeriodRewardDistributionsByIDResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - response := types.QueryClaimPeriodRewardDistributionsByIDResponse{} - sdkCtx := sdk.UnwrapSDKContext(ctx) - - ClaimPeriodReward, err := k.GetClaimPeriodRewardDistribution(sdkCtx, req.GetClaimPeriodId(), req.GetRewardId()) - if err != nil { - return nil, status.Errorf(codes.NotFound, fmt.Sprintf("unable to query claim period reward distributions by ID: %v", err)) - } - - if ClaimPeriodReward.Validate() == nil { - response.ClaimPeriodRewardDistribution = &ClaimPeriodReward - } - - return &response, nil -} - -// RewardDistributionsByAddress returns a list of reward claims belonging to the account and matching the claim status. -func (k Keeper) RewardDistributionsByAddress(ctx context.Context, request *types.QueryRewardDistributionsByAddressRequest) (*types.QueryRewardDistributionsByAddressResponse, error) { - if request == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - address, err := sdk.AccAddressFromBech32(request.Address) - if err != nil { - return nil, sdkerrors.ErrInvalidAddress.Wrap(err.Error()) - } - sdkCtx := sdk.UnwrapSDKContext(ctx) - var states []types.RewardAccountState - getAllRewardAccountStore := prefix.NewStore(sdk.UnwrapSDKContext(ctx).KVStore(k.storeKey), types.GetAllRewardAccountByAddressPartialKey(address)) - - pageRes, err := query.FilteredPaginate(getAllRewardAccountStore, request.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { - lookupVal, errFromParsingKey := types.ParseFilterLookUpKey(key, address) - - if errFromParsingKey != nil { - return false, err - } - result, errFromGetRewardAccount := k.GetRewardAccountState(sdkCtx, lookupVal.RewardID, lookupVal.ClaimID, lookupVal.Addr.String()) - // think ignoring the error maybe ok here since it's just another lookup - if errFromGetRewardAccount != nil { - return false, errFromGetRewardAccount - } - if result.GetSharesEarned() == 0 || (request.ClaimStatus != result.ClaimStatus && request.ClaimStatus != types.RewardAccountState_CLAIM_STATUS_UNSPECIFIED) { - return false, nil - } - - if accumulate { - states = append(states, result) - } - - return true, nil - }) - - if err != nil { - return nil, types.ErrIterateAllRewardAccountStates.Wrap(err.Error()) - } - - rewardAccountResponses := k.convertRewardAccountStateToRewardAccountResponse(sdkCtx, states) - rewardAccountByAddressResponse := types.QueryRewardDistributionsByAddressResponse{ - Address: request.Address, - RewardAccountState: rewardAccountResponses, - Pagination: pageRes, - } - - return &rewardAccountByAddressResponse, nil -} - -func (k Keeper) convertRewardAccountStateToRewardAccountResponse(ctx sdk.Context, states []types.RewardAccountState) []types.RewardAccountResponse { - rewardAccountResponse := make([]types.RewardAccountResponse, 0) - for _, state := range states { - rewardProgram, err := k.GetRewardProgram(ctx, state.GetRewardProgramId()) - if err != nil { - continue - } - distribution, err := k.GetClaimPeriodRewardDistribution(ctx, state.ClaimPeriodId, state.RewardProgramId) - if err != nil { - continue - } - - participantReward := k.CalculateParticipantReward(ctx, int64(state.GetSharesEarned()), distribution.GetTotalShares(), distribution.GetRewardsPool(), rewardProgram.MaxRewardByAddress) - accountResponse := types.RewardAccountResponse{ - RewardProgramId: state.RewardProgramId, - TotalRewardClaim: participantReward, - ClaimStatus: state.ClaimStatus, - ClaimId: state.ClaimPeriodId, - } - rewardAccountResponse = append(rewardAccountResponse, accountResponse) - } - - return rewardAccountResponse -} diff --git a/x/reward/keeper/query_server_test.go b/x/reward/keeper/query_server_test.go deleted file mode 100644 index e1abae2da7..0000000000 --- a/x/reward/keeper/query_server_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package keeper_test - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" - - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestQueryRewardPrograms() { - queryClient := s.queryClient - - response, err := queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{}) - s.Assert().Nil(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 0, "response should contain empty list") - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - s.accountAddr.String(), - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Transfer{ - Transfer: &types.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - rewardProgram.Id = 2 - rewardProgram.State = types.RewardProgram_STATE_STARTED - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - rewardProgram.Id = 3 - rewardProgram.State = types.RewardProgram_STATE_FINISHED - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - rewardProgram.Id = 4 - rewardProgram.State = types.RewardProgram_STATE_EXPIRED - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - response, err = queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{QueryType: types.QueryRewardProgramsRequest_QUERY_TYPE_ALL}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 4, "response should contain the added element") - - response, err = queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{QueryType: types.QueryRewardProgramsRequest_QUERY_TYPE_PENDING}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 1, "response should contain the added element") - s.Assert().Equal(uint64(1), response.RewardPrograms[0].Id, "response should contain pending program") - - response, err = queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{QueryType: types.QueryRewardProgramsRequest_QUERY_TYPE_ACTIVE}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 1, "response should contain the added element") - s.Assert().Equal(uint64(2), response.RewardPrograms[0].Id, "response should contain active program") - - response, err = queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{QueryType: types.QueryRewardProgramsRequest_QUERY_TYPE_OUTSTANDING}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 2, "response should contain the added element") - s.Assert().Equal(uint64(1), response.RewardPrograms[0].Id, "response should contain the pending program") - s.Assert().Equal(uint64(2), response.RewardPrograms[1].Id, "response should contain the active program") - - response, err = queryClient.RewardPrograms(s.ctx.Context(), &types.QueryRewardProgramsRequest{QueryType: types.QueryRewardProgramsRequest_QUERY_TYPE_FINISHED}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(len(response.RewardPrograms), 2, "response should contain the added element") - s.Assert().Equal(uint64(3), response.RewardPrograms[0].Id, "response should contain the finished program") - s.Assert().Equal(uint64(4), response.RewardPrograms[1].Id, "response should contain the expired program") - - responseId, err := queryClient.RewardProgramByID(s.ctx.Context(), &types.QueryRewardProgramByIDRequest{Id: uint64(4)}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(uint64(4), responseId.RewardProgram.Id, "response should contain the reward program with id 4") - - responseId, err = queryClient.RewardProgramByID(s.ctx.Context(), &types.QueryRewardProgramByIDRequest{Id: uint64(1000)}) - s.Assert().Error(err, "query should not error") - s.Assert().Equal(true, responseId == nil, "response should be nil, since program doesn't exist") -} - -func (s *KeeperTestSuite) TestClaimPeriodRewardDistributions() { - - queryClient := s.queryClient - for i := 0; i < 101; i++ { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, types.NewClaimPeriodRewardDistribution(uint64(i+1), 1, sdk.NewInt64Coin("jackthecat", 100), sdk.NewInt64Coin("jackthecat", 10), int64(i), false)) - } - pageRequest := &query.PageRequest{} - pageRequest.Limit = 100 - pageRequest.CountTotal = true - response, err := queryClient.ClaimPeriodRewardDistributions(s.ctx.Context(), &types.QueryClaimPeriodRewardDistributionsRequest{pageRequest}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(response.Pagination.Total, uint64(101)) - s.Assert().Equal(100, len(response.ClaimPeriodRewardDistributions)) - - response, err = queryClient.ClaimPeriodRewardDistributions(s.ctx.Context(), &types.QueryClaimPeriodRewardDistributionsRequest{Pagination: &query.PageRequest{Limit: 10000}}) - s.Assert().NoError(err, "query should not error") - // 0 since pageRequest.CountTotal = false by default - s.Assert().Equal(response.Pagination.Total, uint64(0), "should only return 100") - s.Assert().Equal(101, len(response.ClaimPeriodRewardDistributions), "should only return 100 (max allowed per page)") - - response, err = queryClient.ClaimPeriodRewardDistributions(s.ctx.Context(), &types.QueryClaimPeriodRewardDistributionsRequest{Pagination: &query.PageRequest{Limit: 1, Offset: 9}}) - s.Assert().NoError(err, "query should not error") - // 0 since pageRequest.CountTotal = false by default - s.Assert().Equal(response.Pagination.Total, uint64(0), "should only return 100") - s.Assert().Equal(1, len(response.ClaimPeriodRewardDistributions), "should only return 1") - s.Assert().Equal(uint64(10), response.ClaimPeriodRewardDistributions[0].ClaimPeriodId) -} - -func (s *KeeperTestSuite) TestClaimPeriodRewardDistributionByID() { - - queryClient := s.queryClient - for i := 0; i < 101; i++ { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, types.NewClaimPeriodRewardDistribution(uint64(i+1), 1, sdk.NewInt64Coin("jackthecat", 100), sdk.NewInt64Coin("jackthecat", 10), int64(i), false)) - } - response, err := queryClient.ClaimPeriodRewardDistributionsByID(s.ctx.Context(), &types.QueryClaimPeriodRewardDistributionsByIDRequest{RewardId: uint64(1), ClaimPeriodId: uint64(612)}) - s.Assert().NoError(err, "query should not error") - s.Assert().Nil(response.ClaimPeriodRewardDistribution, "ClaimPeriodRewardDistribution should not be found") - - response, err = queryClient.ClaimPeriodRewardDistributionsByID(s.ctx.Context(), &types.QueryClaimPeriodRewardDistributionsByIDRequest{RewardId: uint64(1), ClaimPeriodId: uint64(99)}) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(response.ClaimPeriodRewardDistribution.RewardProgramId, uint64(1)) - s.Assert().Equal(response.ClaimPeriodRewardDistribution.ClaimPeriodId, uint64(99)) -} - -func (s *KeeperTestSuite) TestRewardDistributionsByAddress() { - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - s.accountAddr.String(), - sdk.NewInt64Coin("nhash", 1000_000_000_000), - sdk.NewInt64Coin("nhash", 10_000_000_000), - time.Now().Add(100*time.Millisecond), - uint64(30), - 10, - 10, - 3, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Transfer{ - Transfer: &types.ActionTransfer{ - MinimumActions: 0, - MaximumActions: 10, - MinimumDelegationAmount: sdk.NewInt64Coin("nhash", 0), - }, - }, - }, - }, - ) - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - queryClient := s.queryClient - for i := 0; i < 402; i++ { - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, types.NewClaimPeriodRewardDistribution(uint64(i+1), 1, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 10), int64(i), false)) - } - - for i := 0; i < 201; i++ { - s.app.RewardKeeper.SetRewardAccountState(s.ctx, types.NewRewardAccountState(1, uint64(i+1), s.accountAddr.String(), 10, []*types.ActionCounter{})) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, types.NewRewardAccountState(1, uint64(i+1), s.accountAddresses[2].String(), 10, []*types.ActionCounter{})) - } - s.Assert().NotEqual(s.accountAddr.String(), s.accountAddresses[2].String()) - pageRequest := &query.PageRequest{} - pageRequest.Limit = 100 - pageRequest.CountTotal = true - response, err := queryClient.RewardDistributionsByAddress(s.ctx.Context(), &types.QueryRewardDistributionsByAddressRequest{ - Address: s.accountAddr.String(), - ClaimStatus: types.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - Pagination: pageRequest, - }) - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(100, len(response.RewardAccountState)) - s.Assert().Equal(uint64(201), response.Pagination.Total) - pageRequest1 := &query.PageRequest{} - pageRequest1.Limit = 100 - pageRequest1.CountTotal = true - s.Assert().NotNil(response.Pagination.NextKey) - pageRequest1.Key = response.Pagination.NextKey - response1, err := queryClient.RewardDistributionsByAddress(s.ctx.Context(), &types.QueryRewardDistributionsByAddressRequest{ - Address: s.accountAddr.String(), - ClaimStatus: types.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - Pagination: pageRequest1, - }) - - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(100, len(response1.RewardAccountState)) - pageRequest2 := &query.PageRequest{} - pageRequest2.Limit = 100 - pageRequest2.CountTotal = true - s.Assert().NotNil(response1.Pagination.NextKey) - pageRequest2.Key = response1.Pagination.NextKey - response2, err := queryClient.RewardDistributionsByAddress(s.ctx.Context(), &types.QueryRewardDistributionsByAddressRequest{ - Address: s.accountAddr.String(), - ClaimStatus: types.RewardAccountState_CLAIM_STATUS_UNSPECIFIED, - Pagination: pageRequest2, - }) - - s.Assert().NoError(err, "query should not error") - s.Assert().Equal(1, len(response2.RewardAccountState)) - -} diff --git a/x/reward/keeper/reward_account_state.go b/x/reward/keeper/reward_account_state.go deleted file mode 100644 index 3a52d4a806..0000000000 --- a/x/reward/keeper/reward_account_state.go +++ /dev/null @@ -1,166 +0,0 @@ -package keeper - -import ( - storetypes "cosmossdk.io/store/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// GetRewardAccountState gets a RewardAccountState. -// If the desired RewardAccountState doesn't exist, an empty RewardAccountState is returned (without error). -func (k Keeper) GetRewardAccountState(ctx sdk.Context, rewardProgramID, rewardClaimPeriodID uint64, addr string) (state types.RewardAccountState, err error) { - store := ctx.KVStore(k.storeKey) - key := types.GetRewardAccountStateKey(rewardProgramID, rewardClaimPeriodID, types.MustAccAddressFromBech32(addr)) - bz := store.Get(key) - if len(bz) == 0 { - return state, nil - } - err = k.cdc.Unmarshal(bz, &state) - - return state, err -} - -// SetRewardAccountState stores the provided RewardAccountState in the state store and indexes it. -func (k Keeper) SetRewardAccountState(ctx sdk.Context, state types.RewardAccountState) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshal(&state) - key := types.GetRewardAccountStateKey(state.GetRewardProgramId(), state.GetClaimPeriodId(), types.MustAccAddressFromBech32(state.GetAddress())) - store.Set(key, bz) - // since there is a significant use case of looking up this via address create a secondary index - // [0x8] :: [addr-bytes::reward program id bytes]::[claim period id bytes] {} - addressLookupKey := types.GetRewardAccountStateAddressLookupKey(types.MustAccAddressFromBech32(state.GetAddress()), state.GetRewardProgramId(), state.GetClaimPeriodId()) - // no need for a value a key can derive all the info needed - store.Set(addressLookupKey, []byte{}) -} - -// IterateRewardAccountStates Iterates over the account states for a reward program's claim period -func (k Keeper) IterateRewardAccountStates(ctx sdk.Context, rewardProgramID, rewardClaimPeriodID uint64, handle func(state types.RewardAccountState) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.GetRewardAccountStateClaimPeriodKey(rewardProgramID, rewardClaimPeriodID)) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - record := types.RewardAccountState{} - if err := k.cdc.Unmarshal(iterator.Value(), &record); err != nil { - return err - } - if handle(record) { - break - } - } - return nil -} - -// IterateRewardAccountStatesByAddress Iterates over the account states by address iterator -func (k Keeper) IterateRewardAccountStatesByAddress(ctx sdk.Context, addr sdk.AccAddress, handle func(state types.RewardAccountState) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.GetAllRewardAccountByAddressPartialKey(types.MustAccAddressFromBech32(addr.String()))) - return k.IterateRewardAccountStatesByLookUpIndex(ctx, addr, iterator, handle) -} - -// IterateRewardAccountStatesByAddressAndRewardsID Iterates over the account states by address iterator and reward id -func (k Keeper) IterateRewardAccountStatesByAddressAndRewardsID(ctx sdk.Context, addr sdk.AccAddress, rewardsID uint64, handle func(state types.RewardAccountState) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.GetAllRewardAccountByAddressAndRewardsIDPartialKey(addr, rewardsID)) - return k.IterateRewardAccountStatesByLookUpIndex(ctx, addr, iterator, handle) -} - -// IterateRewardAccountStatesByLookUpIndex iterates reward account states by secondary index // [0x8] :: [addr-bytes::reward program id bytes]::[claim period id bytes] {} -func (k Keeper) IterateRewardAccountStatesByLookUpIndex(ctx sdk.Context, addr sdk.AccAddress, iterator storetypes.Iterator, handle func(state types.RewardAccountState) (stop bool)) error { - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keyParsed, err := types.ParseRewardAccountLookUpKey(iterator.Key(), addr) - if err != nil { - return err - } - record, err := k.GetRewardAccountState(ctx, keyParsed.RewardID, keyParsed.ClaimID, addr.String()) - if err != nil { - return err - } - if handle(record) { - break - } - } - return nil -} - -// IterateAllRewardAccountStates Iterates over the account states for every reward program -func (k Keeper) IterateAllRewardAccountStates(ctx sdk.Context, handle func(state types.RewardAccountState) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.GetAllRewardAccountStateKey()) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - record := types.RewardAccountState{} - if err := k.cdc.Unmarshal(iterator.Value(), &record); err != nil { - return err - } - if handle(record) { - break - } - } - return nil -} - -// IterateRewardAccountStatesForRewardProgram Iterates over the account states for a reward program -func (k Keeper) IterateRewardAccountStatesForRewardProgram(ctx sdk.Context, rewardProgramID uint64, handle func(state types.RewardAccountState) (stop bool)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.GetRewardProgramRewardAccountStateKey(rewardProgramID)) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - record := types.RewardAccountState{} - if err := k.cdc.Unmarshal(iterator.Value(), &record); err != nil { - return err - } - if handle(record) { - break - } - } - return nil -} - -// Returns a list of account states for the reward program's claim period -func (k Keeper) GetRewardAccountStatesForClaimPeriod(ctx sdk.Context, rewardProgramID, claimPeriodID uint64) ([]types.RewardAccountState, error) { - states := []types.RewardAccountState{} - err := k.IterateRewardAccountStates(ctx, rewardProgramID, claimPeriodID, func(state types.RewardAccountState) (stop bool) { - states = append(states, state) - return false - }) - return states, err -} - -// Returns a list of account states for the reward program -func (k Keeper) GetRewardAccountStatesForRewardProgram(ctx sdk.Context, rewardProgramID uint64) ([]types.RewardAccountState, error) { - states := []types.RewardAccountState{} - err := k.IterateRewardAccountStatesForRewardProgram(ctx, rewardProgramID, func(state types.RewardAccountState) (stop bool) { - states = append(states, state) - return false - }) - return states, err -} - -// Changes the state for all account states in a reward program's claim period to be claimable -func (k Keeper) MakeRewardClaimsClaimableForPeriod(ctx sdk.Context, rewardProgramID, claimPeriodID uint64) error { - states, err := k.GetRewardAccountStatesForClaimPeriod(ctx, rewardProgramID, claimPeriodID) - for _, state := range states { - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - k.SetRewardAccountState(ctx, state) - } - return err -} - -// Changes the state for all account states in a reward program to be expired if they are not claimed -func (k Keeper) ExpireRewardClaimsForRewardProgram(ctx sdk.Context, rewardProgramID uint64) error { - states, err := k.GetRewardAccountStatesForRewardProgram(ctx, rewardProgramID) - for _, state := range states { - if state.ClaimStatus == types.RewardAccountState_CLAIM_STATUS_CLAIMED { - continue - } - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_EXPIRED - k.SetRewardAccountState(ctx, state) - } - return err -} diff --git a/x/reward/keeper/reward_account_state_test.go b/x/reward/keeper/reward_account_state_test.go deleted file mode 100644 index b0dccb4b69..0000000000 --- a/x/reward/keeper/reward_account_state_test.go +++ /dev/null @@ -1,381 +0,0 @@ -package keeper_test - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cometbft/cometbft/crypto/secp256k1" - - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestNewRewardAccountState() { - accountState := types.NewRewardAccountState( - 1, - 2, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - 3, - []*types.ActionCounter{}) - - s.Assert().Equal(uint64(1), accountState.GetRewardProgramId(), "reward program id must match") - s.Assert().Equal(uint64(2), accountState.GetClaimPeriodId(), "reward claim period id must match") - s.Assert().Equal(uint64(3), accountState.GetSharesEarned(), "earned shares must match") - s.Assert().Equal("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", accountState.GetAddress(), "address must match") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_UNCLAIMABLE, accountState.GetClaimStatus(), "should be set to unclaimable initially") - s.Assert().Equal([]*types.ActionCounter{}, accountState.GetActionCounter(), "action counter must match") -} - -func (s *KeeperTestSuite) TestGetSetRewardAccountState() { - expectedState := types.NewRewardAccountState( - 1, - 2, - "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", - 3, - nil, - ) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, expectedState) - actualState, err := s.app.RewardKeeper.GetRewardAccountState(s.ctx, - expectedState.GetRewardProgramId(), - expectedState.GetClaimPeriodId(), - expectedState.GetAddress()) - - s.Assert().Nil(err, "must not have error") - s.Assert().Equal(expectedState.GetRewardProgramId(), actualState.GetRewardProgramId(), "reward program id must match") - s.Assert().Equal(expectedState.GetClaimPeriodId(), actualState.GetClaimPeriodId(), "reward claim period id must match") - s.Assert().Equal(expectedState.GetAddress(), actualState.GetAddress(), "address must match") - s.Assert().Equal(expectedState.GetSharesEarned(), actualState.GetSharesEarned(), "shares earned must match") - s.Assert().Equal(expectedState.GetClaimStatus(), actualState.GetClaimStatus(), "should be set to unclaimed initially") - s.Assert().Equal(expectedState.GetActionCounter(), actualState.GetActionCounter(), "action counter must match") -} - -func (s *KeeperTestSuite) TestGetInvalidAccountState() { - actualState, err := s.app.RewardKeeper.GetRewardAccountState(s.ctx, - 99, - 99, - "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - - s.Assert().Nil(err, "must not have error") - s.Assert().Error(actualState.Validate(), "account state validate basic must return error") -} - -func (s *KeeperTestSuite) TestIterateAccountStates() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStates(s.ctx, 2, 2, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(2, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateAccountStatesByAddress() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - addr, err := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().NoError(err, "no error should be thrown") - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStatesByAddress(s.ctx, addr, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(4, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestEmptyIterateAccountStates() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStates(s.ctx, 1, 4, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(0, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateAccountStatesHalt() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStates(s.ctx, 1, 2, func(state types.RewardAccountState) bool { - counter += 1 - return counter == 1 - })) - - s.Assert().Equal(1, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateAllAccountStates() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(5, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestEmptyIterateAllAccountStates() { - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(0, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateAllAccountStatesHalt() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateAllRewardAccountStates(s.ctx, func(state types.RewardAccountState) bool { - counter += 1 - return counter == 1 - })) - - s.Assert().Equal(1, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateRewardAccountStatesForRewardProgram() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStatesForRewardProgram(s.ctx, 2, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(3, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestEmptyIterateRewardAccountStatesForRewardProgram() { - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStatesForRewardProgram(s.ctx, 1, func(state types.RewardAccountState) bool { - counter += 1 - return false - })) - - s.Assert().Equal(0, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestIterateRewardAccountStatesForRewardProgramHalt() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - counter := 0 - s.Assert().NoError(s.app.RewardKeeper.IterateRewardAccountStatesForRewardProgram(s.ctx, 2, func(state types.RewardAccountState) bool { - counter += 1 - return counter == 1 - })) - - s.Assert().Equal(1, counter, "should have correct number of iterations") -} - -func (s *KeeperTestSuite) TestGetRewardAccountStatesForClaimPeriod() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - states, err := s.app.RewardKeeper.GetRewardAccountStatesForClaimPeriod(s.ctx, 2, 2) - s.Assert().NoError(err, "no error should be thrown when there are account states.") - s.Assert().Equal(2, len(states), "should have correct number of account states") -} - -func (s *KeeperTestSuite) TestGetRewardAccountStatesForClaimPeriodHandlesEmpty() { - states, err := s.app.RewardKeeper.GetRewardAccountStatesForClaimPeriod(s.ctx, 1, 1) - s.Assert().NoError(err, "no error should be thrown when there are no account states.") - s.Assert().Equal(0, len(states), "should have no account states") -} - -func (s *KeeperTestSuite) TestGetRewardAccountStatesForRewardProgram() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - states, err := s.app.RewardKeeper.GetRewardAccountStatesForRewardProgram(s.ctx, 2) - s.Assert().NoError(err, "no error should be thrown when there are account states.") - s.Assert().Equal(3, len(states), "should have correct number of account states") -} - -func (s *KeeperTestSuite) TestGetRewardAccountStatesForRewardProgramHandlesEmpty() { - states, err := s.app.RewardKeeper.GetRewardAccountStatesForClaimPeriod(s.ctx, 1, 1) - s.Assert().NoError(err, "no error should be thrown when there are no account states.") - s.Assert().Equal(0, len(states), "should have no account states") -} - -func (s *KeeperTestSuite) TestMakeRewardClaimsClaimableForPeriod() { - state1 := types.NewRewardAccountState(1, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 3, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(2, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(2, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state5 := types.NewRewardAccountState(2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state5) - - err := s.app.RewardKeeper.MakeRewardClaimsClaimableForPeriod(s.ctx, 2, 2) - s.Assert().NoError(err, "no error should be thrown when there are account states.") - - state1, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state2, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 3, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state3, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 2, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state4, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 2, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state5, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 2, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - - s.Assert().NotEqual(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state1.GetClaimStatus(), "account state should not be updated to be claimable") - s.Assert().NotEqual(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state2.GetClaimStatus(), "account state should not be updated to be claimable") - s.Assert().NotEqual(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state3.GetClaimStatus(), "account state should not be updated to be claimable") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state4.GetClaimStatus(), "account state should not be updated to be claimable") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state5.GetClaimStatus(), "account state should not be updated to be claimable") -} - -func (s *KeeperTestSuite) TestMakeRewardClaimsClaimableForPeriodHandlesEmpty() { - err := s.app.RewardKeeper.MakeRewardClaimsClaimableForPeriod(s.ctx, 1, 1) - s.Assert().NoError(err, "no error should be thrown when there are no account states.") -} - -func (s *KeeperTestSuite) TestExpireRewardClaimsForRewardProgram() { - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(1, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 0, []*types.ActionCounter{}) - state4 := types.NewRewardAccountState(1, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 0, []*types.ActionCounter{}) - state4.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMED - - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state4) - - err := s.app.RewardKeeper.ExpireRewardClaimsForRewardProgram(s.ctx, 1) - s.Assert().NoError(err, "no error should be thrown when there are account states.") - - state1, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - state2, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state3, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 2, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - state4, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_EXPIRED, state1.GetClaimStatus(), "account state should be updated to expired") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_EXPIRED, state2.GetClaimStatus(), "account state should be updated to expired") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_EXPIRED, state3.GetClaimStatus(), "account state should be updated to expired") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_CLAIMED, state4.GetClaimStatus(), "account state should not be updated to expired if claimed") -} - -func (s *KeeperTestSuite) TestExpireRewardClaimsForRewardProgramHandlesEmpty() { - err := s.app.RewardKeeper.ExpireRewardClaimsForRewardProgram(s.ctx, 1) - s.Assert().NoError(err, "no error should be thrown when there are no account states.") -} - -func (s *KeeperTestSuite) TestParseRewardAccountLookUpKey() { - addressFromSec256k1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - rewardProgramId := uint64(123456) - claimPeriodId := uint64(7891011) - accountStateAddressLookupKey := types.GetRewardAccountStateAddressLookupKey(addressFromSec256k1, rewardProgramId, claimPeriodId) - lookup, err := types.ParseRewardAccountLookUpKey(accountStateAddressLookupKey, addressFromSec256k1) - s.Assert().NoError(err, "no error expected for parsing GetRewardAccountStateAddressLookupKey.") - s.Assert().Equal(addressFromSec256k1, lookup.Addr) - s.Assert().Equal(rewardProgramId, lookup.RewardID) - s.Assert().Equal(claimPeriodId, lookup.ClaimID) -} diff --git a/x/reward/keeper/reward_claim.go b/x/reward/keeper/reward_claim.go deleted file mode 100644 index 011aa5c53c..0000000000 --- a/x/reward/keeper/reward_claim.go +++ /dev/null @@ -1,169 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// ClaimRewards for a given address and a given reward program id -func (k Keeper) ClaimRewards(ctx sdk.Context, rewardProgramID uint64, addr string) ([]*types.ClaimedRewardPeriodDetail, sdk.Coin, error) { - rewardProgram, err := k.GetRewardProgram(ctx, rewardProgramID) - if err != nil || rewardProgram.Validate() != nil { - return nil, sdk.Coin{}, fmt.Errorf("reward program %d does not exist", rewardProgramID) - } - - if rewardProgram.State == types.RewardProgram_STATE_EXPIRED { - return nil, sdk.Coin{}, fmt.Errorf("reward program %d has expired", rewardProgramID) - } - - rewards, err := k.claimRewardsForProgram(ctx, rewardProgram, addr) - if err != nil { - return nil, sdk.Coin{}, err - } - sent, err := k.sendRewards(ctx, rewards, addr, rewardProgram.GetTotalRewardPool().Denom) - if err != nil { - return nil, sdk.Coin{}, err - } - rewardProgram.ClaimedAmount = rewardProgram.ClaimedAmount.Add(sent) - k.SetRewardProgram(ctx, rewardProgram) - - return rewards, sent, nil -} - -// claimRewardsForProgram internal method used by ClaimRewards, which iterates over all the reward account states that the -// address is eligible for, and then claim them. -func (k Keeper) claimRewardsForProgram(ctx sdk.Context, rewardProgram types.RewardProgram, addr string) ([]*types.ClaimedRewardPeriodDetail, error) { - var states []types.RewardAccountState - address, err := sdk.AccAddressFromBech32(addr) - if err != nil { - return nil, sdkerrors.ErrInvalidAddress.Wrap(err.Error()) - } - err = k.IterateRewardAccountStatesByAddressAndRewardsID(ctx, address, rewardProgram.GetId(), func(state types.RewardAccountState) bool { - if state.GetSharesEarned() > 0 && state.Address == address.String() { - states = append(states, state) - } - return false - }) - if err != nil { - return nil, err - } - - rewards := make([]*types.ClaimedRewardPeriodDetail, 0, len(states)) - for _, account := range states { - reward, found := k.claimRewardForPeriod(ctx, rewardProgram, account.ClaimPeriodId, addr) - if !found { - continue - } - rewards = append(rewards, &reward) - } - return rewards, nil -} - -// claimRewardForPeriod internal method to actually claim rewards for a period. -func (k Keeper) claimRewardForPeriod(ctx sdk.Context, rewardProgram types.RewardProgram, period uint64, addr string) (reward types.ClaimedRewardPeriodDetail, found bool) { - state, err := k.GetRewardAccountState(ctx, rewardProgram.GetId(), period, addr) - if err != nil { - return reward, false - } - if state.GetClaimStatus() != types.RewardAccountState_CLAIM_STATUS_CLAIMABLE { - return reward, false - } - - distribution, err := k.GetClaimPeriodRewardDistribution(ctx, period, rewardProgram.GetId()) - if err != nil { - return reward, false - } - - participantReward := k.CalculateParticipantReward(ctx, int64(state.GetSharesEarned()), distribution.GetTotalShares(), distribution.GetRewardsPool(), rewardProgram.MaxRewardByAddress) - reward = types.ClaimedRewardPeriodDetail{ - ClaimPeriodId: period, - TotalShares: state.GetSharesEarned(), - ClaimPeriodReward: participantReward, - } - - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMED - k.SetRewardAccountState(ctx, state) - - return reward, true -} - -// sendRewards internal method called with ClaimedRewardPeriodDetail of a single reward program -func (k Keeper) sendRewards(ctx sdk.Context, rewards []*types.ClaimedRewardPeriodDetail, addr string, rewardProgramDenom string) (sdk.Coin, error) { - amount := sdk.NewInt64Coin(rewardProgramDenom, 0) - - if len(rewards) == 0 { - return amount, nil - } - - for _, reward := range rewards { - amount.Denom = reward.GetClaimPeriodReward().Denom - amount = amount.Add(reward.GetClaimPeriodReward()) - } - - return k.sendCoinsToAccount(ctx, amount, addr) -} - -// sendCoinsToAccount internal wrapper method, to mainly do `SendCoinsFromModuleToAccount` -func (k Keeper) sendCoinsToAccount(ctx sdk.Context, amount sdk.Coin, addr string) (sdk.Coin, error) { - if amount.IsZero() { - return sdk.NewInt64Coin(amount.GetDenom(), 0), nil - } - - acc, err := sdk.AccAddressFromBech32(addr) - if err != nil { - return sdk.NewInt64Coin(amount.Denom, 0), err - } - - err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, acc, sdk.NewCoins(amount)) - if err != nil { - return sdk.NewInt64Coin(amount.Denom, 0), err - } - - return amount, nil -} - -// RefundRewardClaims refund all unclaimed rewards to the reward program creator -func (k Keeper) RefundRewardClaims(ctx sdk.Context, rewardProgram types.RewardProgram) error { - amount := rewardProgram.TotalRewardPool.Sub(rewardProgram.RemainingPoolBalance).Sub(rewardProgram.ClaimedAmount) - _, err := k.sendCoinsToAccount(ctx, amount, rewardProgram.GetDistributeFromAddress()) - return err -} - -// ClaimAllRewards calls ClaimRewards, however differs from ClaimRewards in that it claims all the rewards that the address -// is eligible for across all reward programs. -func (k Keeper) ClaimAllRewards(ctx sdk.Context, addr string) ([]*types.RewardProgramClaimDetail, sdk.Coins, error) { - allProgramDetails := []*types.RewardProgramClaimDetail{} - allRewards := sdk.Coins{} - - programs, err := k.GetAllUnexpiredRewardPrograms(ctx) - if err != nil { - return nil, sdk.Coins{}, err - } - - for _, rewardProgram := range programs { - details, reward, err := k.ClaimRewards(ctx, rewardProgram.GetId(), addr) - // err needs to propagated up, else tx will commit - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Unable to claim reward program %d. Error: %v ", rewardProgram.GetId(), err)) - return nil, sdk.Coins{}, err - } - if reward.IsZero() { - ctx.Logger().Info(fmt.Sprintf("Skipping reward program %d. It has no rewards.", rewardProgram.GetId())) - continue - } - - programDetails := types.RewardProgramClaimDetail{ - RewardProgramId: rewardProgram.GetId(), - TotalRewardClaim: reward, - ClaimedRewardPeriodDetails: details, - } - allProgramDetails = append(allProgramDetails, &programDetails) - allRewards = allRewards.Add(reward) - } - - return allProgramDetails, allRewards, nil -} diff --git a/x/reward/keeper/reward_claim_test.go b/x/reward/keeper/reward_claim_test.go deleted file mode 100644 index aadd5170e1..0000000000 --- a/x/reward/keeper/reward_claim_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package keeper_test - -import ( - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -var ( - minDelegation = sdk.NewInt64Coin("nhash", 4) - maxDelegation = sdk.NewInt64Coin("nhash", 40) -) - -func (s *KeeperTestSuite) TestClaimRewards() { - time := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 3, - 0, - uint64(time.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for i := 1; i <= int(rewardProgram.GetClaimPeriods()); i++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(i), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(i), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - - details, reward, err := s.app.RewardKeeper.ClaimRewards(s.ctx, rewardProgram.GetId(), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().NoError(err, "should throw no error") - - rewardProgram, err = s.app.RewardKeeper.GetRewardProgram(s.ctx, rewardProgram.GetId()) - s.Assert().NoError(err, "should throw no error") - s.Assert().Equal(3, len(details), "should have rewards from every period") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 300), reward, "should total up the rewards from the periods") -} - -func (s *KeeperTestSuite) TestClaimRewardsHandlesInvalidProgram() { - time := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 5, - 0, - uint64(time.Day()), - []types.QualifyingAction{}, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = 5 - - details, reward, err := s.app.RewardKeeper.ClaimRewards(s.ctx, rewardProgram.GetId(), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().Nil(details, "should have no reward details") - s.Assert().Equal(sdk.Coin{}, reward, "should have no reward") - s.Assert().Error(err, "should throw error") -} - -func (s *KeeperTestSuite) TestClaimRewardsHandlesExpiredProgram() { - time := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 5, - 0, - uint64(time.Day()), - []types.QualifyingAction{}, - ) - rewardProgram.State = types.RewardProgram_STATE_EXPIRED - rewardProgram.CurrentClaimPeriod = 5 - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - details, reward, err := s.app.RewardKeeper.ClaimRewards(s.ctx, rewardProgram.GetId(), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().Nil(details, "should have no reward details") - s.Assert().Equal(sdk.Coin{}, reward, "should have no reward") - s.Assert().Error(err, "should throw error") -} - -func (s *KeeperTestSuite) TestRefundRewardClaims() { - time := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 5, - 0, - uint64(time.Day()), - []types.QualifyingAction{}, - ) - rewardProgram.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 0) - rewardProgram.ClaimedAmount = sdk.NewInt64Coin("nhash", 0) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - err := s.app.RewardKeeper.RefundRewardClaims(s.ctx, rewardProgram) - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(beforeBalance.Add(rewardProgram.TotalRewardPool), afterBalance, "unclaimed balance should be refunded") -} - -func (s *KeeperTestSuite) TestRefundRewardClaimsEmpty() { - time := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 5, - 0, - uint64(time.Day()), - []types.QualifyingAction{}, - ) - rewardProgram.RemainingPoolBalance = rewardProgram.GetTotalRewardPool() - rewardProgram.ClaimedAmount = sdk.NewInt64Coin("nhash", 0) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - err := s.app.RewardKeeper.RefundRewardClaims(s.ctx, rewardProgram) - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(beforeBalance, afterBalance, "balance should stay same since all claims are taken") -} - -func (s *KeeperTestSuite) TestClaimAllRewards() { - time := s.ctx.BlockTime() - - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 3, - 0, - uint64(time.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for j := 1; j <= int(rewardProgram.GetClaimPeriods()); j++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(j), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_CLAIMABLE - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(j), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - } - - details, reward, err := s.app.RewardKeeper.ClaimAllRewards(s.ctx, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().NoError(err, "should throw no error") - s.Assert().Equal(3, len(details), "should have rewards from every program") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 900), reward[0], "should total up the rewards from the periods") - - for i := 0; i < len(details); i++ { - s.Assert().Equal(3, len(details[i].ClaimedRewardPeriodDetails), "should have claims from every period") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 300), details[i].TotalRewardClaim, "should total up the rewards from the periods") - s.Assert().Equal(uint64(i+1), details[i].RewardProgramId, "should have the correct id") - } -} - -func (s *KeeperTestSuite) TestClaimAllRewardsExpired() { - time := s.ctx.BlockTime() - - for i := 0; i < 3; i++ { - rewardProgram := types.NewRewardProgram( - "title", - "description", - uint64(i+1), - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - time, - 10, - 3, - 0, - uint64(time.Day()), - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.State = types.RewardProgram_STATE_EXPIRED - rewardProgram.CurrentClaimPeriod = rewardProgram.GetClaimPeriods() - s.app.RewardKeeper.SetRewardProgram(s.ctx, rewardProgram) - - for j := 1; j <= int(rewardProgram.GetClaimPeriods()); j++ { - state := types.NewRewardAccountState(rewardProgram.GetId(), uint64(j), "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 1, []*types.ActionCounter{}) - state.ClaimStatus = types.RewardAccountState_CLAIM_STATUS_EXPIRED - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - distribution := types.NewClaimPeriodRewardDistribution(uint64(j), rewardProgram.GetId(), sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, true) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, distribution) - } - } - - details, reward, err := s.app.RewardKeeper.ClaimAllRewards(s.ctx, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().NoError(err, "should throw no error") - s.Assert().Equal(0, len(details), "should have rewards from every program") - s.Assert().Equal(0, len(reward), "should total up the rewards from the periods") -} - -func (s *KeeperTestSuite) TestClaimAllRewardsNoPrograms() { - details, reward, err := s.app.RewardKeeper.ClaimAllRewards(s.ctx, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - s.Assert().NoError(err, "should throw no error") - s.Assert().Equal(0, len(details), "should have rewards from every program") - s.Assert().Equal(0, len(reward), "should total up the rewards from the periods") -} diff --git a/x/reward/keeper/reward_program.go b/x/reward/keeper/reward_program.go deleted file mode 100644 index 80eaf9e510..0000000000 --- a/x/reward/keeper/reward_program.go +++ /dev/null @@ -1,196 +0,0 @@ -package keeper - -import ( - "fmt" - - storetypes "cosmossdk.io/store/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// CreateRewardProgram with the rewards creator funding the creation of the program. -func (k Keeper) CreateRewardProgram(ctx sdk.Context, rewardProgram types.RewardProgram) (err error) { - err = rewardProgram.Validate() - if err != nil { - return err - } - - blockTime := ctx.BlockTime().UTC() - proposedStartTime := rewardProgram.ProgramStartTime.UTC() - if !types.TimeOnOrAfter(blockTime, proposedStartTime) { - return fmt.Errorf("start time is before current block time %v : %v ", blockTime, proposedStartTime) - } - // error check done in reward Validate() - acc, _ := sdk.AccAddressFromBech32(rewardProgram.DistributeFromAddress) - err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, acc, types.ModuleName, sdk.NewCoins(rewardProgram.TotalRewardPool)) - if err != nil { - return fmt.Errorf("unable to send coin to module reward pool : %w", err) - } - k.SetRewardProgram(ctx, rewardProgram) - return nil -} - -// EndingRewardProgram end reward program preemptively, can only be done by reward program creator. -func (k Keeper) EndingRewardProgram(ctx sdk.Context, rewardProgram types.RewardProgram) { - if rewardProgram.State == types.RewardProgram_STATE_STARTED { - rewardProgram.ClaimPeriods = rewardProgram.CurrentClaimPeriod - rewardProgram.MaxRolloverClaimPeriods = 0 - rewardProgram.ExpectedProgramEndTime = rewardProgram.ClaimPeriodEndTime - rewardProgram.ProgramEndTimeMax = rewardProgram.ClaimPeriodEndTime - k.SetRewardProgram(ctx, rewardProgram) - } else if rewardProgram.State == types.RewardProgram_STATE_PENDING { - k.RemoveRewardProgram(ctx, rewardProgram.Id) - } -} - -// SetRewardProgram sets the reward program in the keeper -func (k Keeper) SetRewardProgram(ctx sdk.Context, rewardProgram types.RewardProgram) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshal(&rewardProgram) - store.Set(types.GetRewardProgramKey(rewardProgram.Id), bz) -} - -// RemoveRewardProgram Removes a reward program in the keeper -func (k Keeper) RemoveRewardProgram(ctx sdk.Context, id uint64) bool { - store := ctx.KVStore(k.storeKey) - key := types.GetRewardProgramKey(id) - keyExists := store.Has(key) - if keyExists { - store.Delete(key) - } - return keyExists -} - -// GetRewardProgram returns a RewardProgram by id -func (k Keeper) GetRewardProgram(ctx sdk.Context, id uint64) (rewardProgram types.RewardProgram, err error) { - store := ctx.KVStore(k.storeKey) - key := types.GetRewardProgramKey(id) - bz := store.Get(key) - if len(bz) == 0 { - return rewardProgram, types.ErrRewardProgramNotFound - } - err = k.cdc.Unmarshal(bz, &rewardProgram) - return rewardProgram, err -} - -// IterateRewardPrograms iterates all reward programs with the given handler function. -func (k Keeper) IterateRewardPrograms(ctx sdk.Context, handle func(rewardProgram types.RewardProgram) (stop bool, err error)) error { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.RewardProgramKeyPrefix) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - record := types.RewardProgram{} - if err := k.cdc.Unmarshal(iterator.Value(), &record); err != nil { - return err - } - stop, err := handle(record) - if err != nil { - return err - } - if stop { - break - } - } - return nil -} - -// GetAllOutstandingRewardPrograms Gets all RewardPrograms that have not expired -func (k Keeper) GetAllOutstandingRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_PENDING, types.RewardProgram_STATE_STARTED) -} - -// GetAllActiveRewardPrograms gets all RewardPrograms that have started -func (k Keeper) GetAllActiveRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_STARTED) -} - -// GetAllCompletedRewardPrograms gets all completed the RewardPrograms -func (k Keeper) GetAllCompletedRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_FINISHED, types.RewardProgram_STATE_EXPIRED) -} - -// GetAllPendingRewardPrograms gets all pending the RewardPrograms -func (k Keeper) GetAllPendingRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_PENDING) -} - -// GetAllExpiredRewardPrograms gets all expired RewardPrograms -func (k Keeper) GetAllExpiredRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_EXPIRED) -} - -// GetAllUnexpiredRewardPrograms gets all RewardPrograms that are not expired -func (k Keeper) GetAllUnexpiredRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - return k.getRewardProgramByState(ctx, types.RewardProgram_STATE_PENDING, types.RewardProgram_STATE_STARTED, types.RewardProgram_STATE_FINISHED) -} - -// getRewardProgramByState gets rewards based on state -func (k Keeper) getRewardProgramByState(ctx sdk.Context, states ...types.RewardProgram_State) ([]types.RewardProgram, error) { - var rewardPrograms []types.RewardProgram - // get all the rewards programs by state - err := k.IterateRewardPrograms(ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - for _, state := range states { - if rewardProgram.GetState() == state { - rewardPrograms = append(rewardPrograms, rewardProgram) - break - } - } - - return false, nil - }) - return rewardPrograms, err -} - -// GetAllRewardPrograms Gets all the RewardPrograms -func (k Keeper) GetAllRewardPrograms(ctx sdk.Context) ([]types.RewardProgram, error) { - var rewardPrograms []types.RewardProgram - err := k.IterateRewardPrograms(ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - rewardPrograms = append(rewardPrograms, rewardProgram) - return false, nil - }) - if err != nil { - return nil, err - } - return rewardPrograms, nil -} - -// GetRewardProgramID gets the highest rewardprogram ID -func (k Keeper) GetRewardProgramID(ctx sdk.Context) (rewardprogramID uint64, err error) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.RewardProgramIDKey) - if bz == nil { - return 1, nil - } - - rewardprogramID = types.GetRewardProgramIDFromBytes(bz) - return rewardprogramID, nil -} - -// setRewardProgramID sets the new rewardprogram ID to the store -func (k Keeper) setRewardProgramID(ctx sdk.Context, rewardprogramID uint64) { - store := ctx.KVStore(k.storeKey) - store.Set(types.RewardProgramIDKey, types.GetRewardProgramIDBytes(rewardprogramID)) -} - -// GetNextRewardProgramID returns the next available reward program ID and increments keeper with next reward program ID -func (k Keeper) GetNextRewardProgramID(ctx sdk.Context) (rewardProgramID uint64, err error) { - rewardProgramID, err = k.GetRewardProgramID(ctx) - if err == nil { - k.setRewardProgramID(ctx, rewardProgramID+1) - } - return rewardProgramID, err -} - -// RefundRemainingBalance returns the remaining pool balance to the reward program creator -func (k Keeper) RefundRemainingBalance(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - _, err := k.sendCoinsToAccount(ctx, rewardProgram.GetRemainingPoolBalance(), rewardProgram.GetDistributeFromAddress()) - if err != nil { - return err - } - - rewardProgram.RemainingPoolBalance = sdk.NewInt64Coin(rewardProgram.RemainingPoolBalance.GetDenom(), 0) - return nil -} diff --git a/x/reward/keeper/reward_program_test.go b/x/reward/keeper/reward_program_test.go deleted file mode 100644 index 5440d5f2a3..0000000000 --- a/x/reward/keeper/reward_program_test.go +++ /dev/null @@ -1,869 +0,0 @@ -package keeper_test - -import ( - "strings" - "time" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestNewRewardProgram() { - now := time.Now().UTC() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - - s.Assert().Equal("title", program.GetTitle(), "title should match input") - s.Assert().Equal("description", program.GetDescription(), "description should match input") - s.Assert().Equal(uint64(1), program.GetId(), "id should match input") - s.Assert().Equal("insert address", program.GetDistributeFromAddress(), "address should match input") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 100000), program.GetTotalRewardPool(), "coin should match input") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 1000), program.GetMaxRewardByAddress(), "max reward by address should match") - s.Assert().Equal(now.UTC(), program.GetProgramStartTime(), "program start time should match input") - s.Assert().Equal(uint64(60*60), program.GetClaimPeriodSeconds(), "claim period seconds should match input") - s.Assert().Equal(uint64(3), program.GetClaimPeriods(), "claim periods should match input") - s.Assert().Equal(0, len(program.GetQualifyingActions()), "qualifying actions should match input") -} - -func (s *KeeperTestSuite) TestGetSetRewardProgram() { - now := time.Now().Local().UTC() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program) - program2, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 1) - - s.Assert().NoError(err, "no error should be returned when getting reward program") - - s.Assert().Equal(program.GetTitle(), program2.GetTitle(), "title should match") - s.Assert().Equal(program.GetDescription(), program2.GetDescription(), "description should match") - s.Assert().Equal(program.GetId(), program2.GetId(), "id should match") - s.Assert().Equal(program.GetDistributeFromAddress(), program2.GetDistributeFromAddress(), "address should match") - s.Assert().Equal(program.GetTotalRewardPool(), program2.GetTotalRewardPool(), "coin should match") - s.Assert().Equal(program.GetMaxRewardByAddress(), program2.GetMaxRewardByAddress(), "max reward by address should") - s.Assert().Equal(program.GetProgramStartTime(), program2.GetProgramStartTime(), "program start time should match") - s.Assert().Equal(program.GetClaimPeriodSeconds(), program2.GetClaimPeriodSeconds(), "claim period seconds should match") - s.Assert().Equal(program.GetClaimPeriods(), program2.GetClaimPeriods(), "number of claim periods should match") - s.Assert().Equal(len(program.GetQualifyingActions()), len(program2.GetQualifyingActions()), "qualifying actions should match") -} - -func (s *KeeperTestSuite) TestEndingRewardProgram() { - now := time.Now() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 3, - 0, - []types.QualifyingAction{}, - ) - program.State = types.RewardProgram_STATE_STARTED - program.Id = 10 - - program.CurrentClaimPeriod = 2 - program.ClaimPeriodEndTime = now - s.app.RewardKeeper.SetRewardProgram(s.ctx, program) - s.app.RewardKeeper.EndingRewardProgram(s.ctx, program) - - endingRewardProgram, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 10) - s.Assert().NoError(err) - s.Assert().Equal(uint64(2), endingRewardProgram.ClaimPeriods) - s.Assert().Equal(uint64(0), endingRewardProgram.MaxRolloverClaimPeriods) - s.Assert().Equal(now.UTC(), endingRewardProgram.ExpectedProgramEndTime) - s.Assert().Equal(now.UTC(), endingRewardProgram.ProgramEndTimeMax) - - program.State = types.RewardProgram_STATE_PENDING - program.Id = 20 - s.app.RewardKeeper.SetRewardProgram(s.ctx, program) - endingRewardProgram, err = s.app.RewardKeeper.GetRewardProgram(s.ctx, 20) - s.Assert().NoError(err) - s.Assert().Equal(uint64(20), endingRewardProgram.Id) - - s.app.RewardKeeper.EndingRewardProgram(s.ctx, program) - endingRewardProgram, err = s.app.RewardKeeper.GetRewardProgram(s.ctx, 20) - s.Assert().Error(err) - s.Assert().Equal(uint64(0), endingRewardProgram.Id) -} - -func (s *KeeperTestSuite) TestRemoveValidRewardProgram() { - now := time.Now() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program) - removed := s.app.RewardKeeper.RemoveRewardProgram(s.ctx, 1) - s.Assert().True(removed, "remove should succeed") - - invalidProgram, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 1) - s.Assert().Error(err) - s.Assert().Equal(uint64(0), invalidProgram.Id) -} - -func (s *KeeperTestSuite) TestRemoveInvalidRewardProgram() { - invalidProgram, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, 1) - s.Assert().Error(err) - s.Assert().Equal(uint64(0), invalidProgram.Id) -} - -func (s *KeeperTestSuite) TestIterateRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - counter := 0 - err := s.app.RewardKeeper.IterateRewardPrograms(s.ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - counter += 1 - return false, nil - }) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(3, counter, "should iterate through each reward program") -} - -func (s *KeeperTestSuite) TestIterateRewardProgramsHalt() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - counter := 0 - err := s.app.RewardKeeper.IterateRewardPrograms(s.ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - counter += 1 - return true, nil - }) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(1, counter, "should stop when iteration is instructed to stop") -} - -func (s *KeeperTestSuite) TestIterateRewardProgramsEmpty() { - counter := 0 - err := s.app.RewardKeeper.IterateRewardPrograms(s.ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - counter += 1 - return true, nil - }) - - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, counter, "should stop when iteration is instructed to stop") -} - -func (s *KeeperTestSuite) TestGetAllOutstandingRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2.State = types.RewardProgram_STATE_STARTED - program3.State = types.RewardProgram_STATE_FINISHED - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - programs, err := s.app.RewardKeeper.GetAllOutstandingRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(2, len(programs), "should have all outstanding programs") - s.Assert().Equal(uint64(1), programs[0].GetId(), "should have program 1") - s.Assert().Equal(uint64(2), programs[1].GetId(), "should have program 2") -} - -func (s *KeeperTestSuite) TestGetAllOutstandingRewardProgramsEmpty() { - programs, err := s.app.RewardKeeper.GetAllOutstandingRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, len(programs), "should have no outstanding programs") -} - -func (s *KeeperTestSuite) TestGetAllExpiredRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program4 := types.NewRewardProgram( - "title", - "description", - 4, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program5 := types.NewRewardProgram( - "title", - "description", - 5, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2.State = types.RewardProgram_STATE_STARTED - program3.State = types.RewardProgram_STATE_FINISHED - program4.State = types.RewardProgram_STATE_EXPIRED - program5.State = types.RewardProgram_STATE_EXPIRED - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program4) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program5) - - programs, err := s.app.RewardKeeper.GetAllExpiredRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(2, len(programs), "should have all outstanding programs") - s.Assert().Equal(uint64(4), programs[0].GetId(), "should have program 4") - s.Assert().Equal(uint64(5), programs[1].GetId(), "should have program 5") -} - -func (s *KeeperTestSuite) TestGetAllExpiredRewardProgramsEmpty() { - programs, err := s.app.RewardKeeper.GetAllExpiredRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, len(programs), "should have no expired programs") -} - -func (s *KeeperTestSuite) TestGetAllUnexpiredRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program4 := types.NewRewardProgram( - "title", - "description", - 4, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program5 := types.NewRewardProgram( - "title", - "description", - 5, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2.State = types.RewardProgram_STATE_STARTED - program3.State = types.RewardProgram_STATE_FINISHED - program4.State = types.RewardProgram_STATE_EXPIRED - program5.State = types.RewardProgram_STATE_EXPIRED - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program4) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program5) - - programs, err := s.app.RewardKeeper.GetAllUnexpiredRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(3, len(programs), "should have all unexpired programs") - s.Assert().Equal(uint64(1), programs[0].GetId(), "should have program 1") - s.Assert().Equal(uint64(2), programs[1].GetId(), "should have program 2") - s.Assert().Equal(uint64(3), programs[2].GetId(), "should have program 3") -} - -func (s *KeeperTestSuite) TestGetAllUnexpiredRewardProgramsEmpty() { - programs, err := s.app.RewardKeeper.GetAllUnexpiredRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, len(programs), "should have no expired programs") -} - -func (s *KeeperTestSuite) TestGetAllActiveRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2.State = types.RewardProgram_STATE_STARTED - program3.State = types.RewardProgram_STATE_FINISHED - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - programs, err := s.app.RewardKeeper.GetAllActiveRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(1, len(programs), "should have all active programs") - s.Assert().Equal(uint64(2), programs[0].GetId(), "should have program 2") -} - -func (s *KeeperTestSuite) TestGetAllActiveRewardProgramsEmpty() { - programs, err := s.app.RewardKeeper.GetAllActiveRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, len(programs), "should have no active programs") -} - -func (s *KeeperTestSuite) TestGetAllRewardPrograms() { - now := time.Now() - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2.State = types.RewardProgram_STATE_STARTED - program3.State = types.RewardProgram_STATE_FINISHED - - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - programs, err := s.app.RewardKeeper.GetAllRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(3, len(programs), "should have all active programs") - s.Assert().Equal(uint64(1), programs[0].GetId(), "should have program 1") - s.Assert().Equal(uint64(2), programs[1].GetId(), "should have program 2") - s.Assert().Equal(uint64(3), programs[2].GetId(), "should have program 3") -} - -func (s *KeeperTestSuite) TestGetAllRewardProgramsEmpty() { - programs, err := s.app.RewardKeeper.GetAllRewardPrograms(s.ctx) - s.Assert().NoError(err, "no error should be returned") - s.Assert().Equal(0, len(programs), "should have no active programs") -} - -func (s *KeeperTestSuite) TestCreateRewardProgram() { - testutil.FundAccount(s.ctx, s.app.BankKeeper, s.accountAddresses[0], sdk.NewCoins(sdk.NewInt64Coin("nhash", 1000000000000))) - - err := s.app.RewardKeeper.CreateRewardProgram(s.ctx, types.RewardProgram{}) - s.Assert().Error(err) - - now := time.Now() - validProgram := types.NewRewardProgram( - "title", - "description", - 1, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - err = s.app.RewardKeeper.CreateRewardProgram(s.ctx, validProgram) - s.Assert().NoError(err) - actualProgram, err := s.app.RewardKeeper.GetRewardProgram(s.ctx, uint64(1)) - s.Assert().NoError(err) - s.Equal(uint64(1), actualProgram.Id) - lastYear := now.Add(-60 * 60 * 365 * time.Second) - inValidProgramStartTime := types.NewRewardProgram( - "title", - "description", - 2, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - lastYear, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - err = s.app.RewardKeeper.CreateRewardProgram(s.ctx, inValidProgramStartTime) - s.Assert().Error(err) - s.Assert().True(strings.Contains(err.Error(), "start time is before current block time")) - - minDelegation := sdk.NewInt64Coin("nhash", 4) - maxDelegation := sdk.NewInt64Coin("nhash", 40) - - invalidAmount := types.NewRewardProgram( - "title", - "description", - 2, - s.accountAddresses[0].String(), - sdk.NewInt64Coin("nhash", 10000000000000), - sdk.NewInt64Coin("nhash", 1000), - now, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - err = s.app.RewardKeeper.CreateRewardProgram(s.ctx, invalidAmount) - s.Assert().ErrorContains(err, "unable to send coin to module reward pool : spendable balance 999999900000nhash is smaller than 10000000000000nhash: insufficient funds") -} - -func (s *KeeperTestSuite) TestRefundRemainingBalance() { - now := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 5, - 0, - uint64(now.Day()), - []types.QualifyingAction{}, - ) - remainingBalance := rewardProgram.GetTotalRewardPool() - rewardProgram.RemainingPoolBalance = remainingBalance - rewardProgram.ClaimedAmount = sdk.NewInt64Coin("nhash", 0) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - err := s.app.RewardKeeper.RefundRemainingBalance(s.ctx, &rewardProgram) - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), rewardProgram.GetRemainingPoolBalance(), "no remaining balance should be left") - s.Assert().Equal(beforeBalance.Add(remainingBalance), afterBalance, "balance should be given remaining pool balance") -} - -func (s *KeeperTestSuite) TestRefundRemainingBalanceEmpty() { - now := s.ctx.BlockTime() - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 100), - now, - 10, - 5, - 0, - uint64(now.Day()), - []types.QualifyingAction{}, - ) - rewardProgram.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 0) - rewardProgram.ClaimedAmount = sdk.NewInt64Coin("nhash", 0) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - err := s.app.RewardKeeper.RefundRemainingBalance(s.ctx, &rewardProgram) - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), rewardProgram.GetRemainingPoolBalance(), "no remaining balance should be left") - s.Assert().Equal(beforeBalance, afterBalance, "balance should remain same because there is no remaining pool balance") -} - -func (s *KeeperTestSuite) TestGetRewardProgramID() { - id, err := s.app.RewardKeeper.GetRewardProgramID(s.ctx) - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(uint64(1), id, "id should match") - - next, err := s.app.RewardKeeper.GetNextRewardProgramID(s.ctx) - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(uint64(1), next, "id should match") - - id, err = s.app.RewardKeeper.GetRewardProgramID(s.ctx) - s.Assert().NoError(err, "no error should be thrown") - s.Assert().Equal(uint64(2), id, "id should match") -} diff --git a/x/reward/keeper/rules.go b/x/reward/keeper/rules.go deleted file mode 100644 index 5c16732baa..0000000000 --- a/x/reward/keeper/rules.go +++ /dev/null @@ -1,279 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// CacheContextWithHistory returns a new Context with the multi-store cached and a new -// EventManager with existing history. The cached context is written to the context when writeCache -// is called. Note, events are automatically emitted on the parent context's -// EventManager when the caller executes the write. -// -// This is similar to the ctx.CacheContext() function, but this keeps the history so we can act on it. -func CacheContextWithHistory(ctx sdk.Context) (cc sdk.Context, writeCache func()) { - cms := ctx.MultiStore().CacheMultiStore() - cc = ctx.WithMultiStore(cms) - abciEventHistory, ok := ctx.EventManager().(sdk.EventManagerWithHistoryI) - if !ok { - panic("event manager does not implement EventManagerWithHistoryI") - } - cc = cc.WithEventManager(sdk.NewEventManagerWithHistory(abciEventHistory.GetABCIEventHistory())) - - writeCache = func() { - ctx.EventManager().EmitEvents(cc.EventManager().Events()) - cms.Write() - } - - return cc, writeCache -} - -// ProcessTransactions in the endblock -func (k Keeper) ProcessTransactions(origCtx sdk.Context) { - // Get all Active Reward Programs - rewardPrograms, err := k.GetAllActiveRewardPrograms(origCtx) - if err != nil { - origCtx.Logger().Error(err.Error()) - return - } - - // Grant shares for qualifying actions - for index := range rewardPrograms { - // Because this is designed for the EndBlocker, we don't always have auto-rollback. - // We don't partial state recorded if an error is encountered in the middle. - // So use a cache context and only write it if there wasn't an error. - ctx, writeCtx := CacheContextWithHistory(origCtx) - // Go through all the reward programs - actions, err := k.DetectQualifyingActions(ctx, &rewardPrograms[index]) - if err != nil { - ctx.Logger().Error(err.Error()) - continue - } - - // Record any results - err = k.RewardShares(ctx, &rewardPrograms[index], actions) - if err != nil { - ctx.Logger().Error(err.Error()) - continue - } - - writeCtx() - } -} - -// DetectQualifyingActions takes in the RewardProgram and checks if any of the qualifying actions is found in the event history -func (k Keeper) DetectQualifyingActions(ctx sdk.Context, program *types.RewardProgram) ([]types.EvaluationResult, error) { - ctx.Logger().Info(fmt.Sprintf("EvaluateRules for RewardProgram: %d", program.GetId())) - results := []types.EvaluationResult(nil) - - // Check if any of the transactions are qualifying actions - for _, supportedAction := range program.GetQualifyingActions() { - // Get the appropriate RewardAction - // If it's not supported we skip it - action, err := supportedAction.GetRewardAction(ctx) - if err != nil { - ctx.Logger().Info(err.Error()) - continue - } - - // Build all the qualifying actions from the abci events - actions, err := k.FindQualifyingActions(ctx, action) - if err != nil { - return nil, err - } - - // Process actions and get the results - actions = k.ProcessQualifyingActions(ctx, program, action, actions) - results = append(results, actions...) - } - - return results, nil -} - -// ProcessQualifyingActions process the detected qualifying actions. -func (k Keeper) ProcessQualifyingActions(ctx sdk.Context, program *types.RewardProgram, processor types.RewardAction, actions []types.EvaluationResult) []types.EvaluationResult { - successfulActions := []types.EvaluationResult(nil) - if program == nil || processor == nil || actions == nil { - return successfulActions - } - - for _, action := range actions { - state, err := k.GetRewardAccountState(ctx, program.GetId(), program.GetCurrentClaimPeriod(), action.Address.String()) - if err != nil { - continue - } - if state.Validate() != nil { - state = types.NewRewardAccountState(program.GetId(), program.GetCurrentClaimPeriod(), action.Address.String(), 0, []*types.ActionCounter{}) - } - - if !processor.PreEvaluate(ctx, k, state) { - k.SetRewardAccountState(ctx, state) - continue - } - if !processor.Evaluate(ctx, k, state, action) { - k.SetRewardAccountState(ctx, state) - continue - } - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, processor.ActionType()) - postEvalRes, evaluationResultFromPostEval := processor.PostEvaluate(ctx, k, state, action) - if !postEvalRes { - k.SetRewardAccountState(ctx, state) - continue - } - - successfulActions = append(successfulActions, evaluationResultFromPostEval) - k.SetRewardAccountState(ctx, state) - } - - return successfulActions -} - -// RewardShares Sets shares for an account(i.e address) based on EvaluationResult -func (k Keeper) RewardShares(ctx sdk.Context, rewardProgram *types.RewardProgram, evaluateRes []types.EvaluationResult) error { - ctx.Logger().Info(fmt.Sprintf("Recording shares for for rewardProgramId=%d, claimPeriod=%d", - rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod())) - - if rewardProgram == nil { - return sdkerrors.ErrNotFound.Wrap("reward program cannot be nil") - } - - // get the ClaimPeriodRewardDistribution - claimPeriodRewardDistribution, err := k.GetClaimPeriodRewardDistribution(ctx, rewardProgram.GetCurrentClaimPeriod(), rewardProgram.GetId()) - - if err != nil { - return err - } - - if claimPeriodRewardDistribution.Validate() != nil { - return sdkerrors.ErrNotFound.Wrap("invalid claim period reward distribution") - } - - for _, res := range evaluateRes { - state, err := k.GetRewardAccountState(ctx, rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), res.Address.String()) - if state.Validate() != nil { - ctx.Logger().Error(fmt.Sprintf("Account state does not exist for RewardProgram: %d, ClaimPeriod: %d, Address: %s. Skipping...", - rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), res.Address.String())) - continue - } - if err != nil { - return err - } - - state.SharesEarned += uint64(res.Shares) - k.SetRewardAccountState(ctx, state) - // we know the rewards, so update the claim period reward - claimPeriodRewardDistribution.TotalShares += res.Shares - } - - // set total claim period rewards distribution. - k.SetClaimPeriodRewardDistribution(ctx, claimPeriodRewardDistribution) - - return nil -} - -// IterateABCIEvents Iterates through all the ABCIEvents that match the eventCriteria. -// Nil criteria means to iterate over everything. -func (k Keeper) IterateABCIEvents(ctx sdk.Context, criteria *types.EventCriteria, action func(string, *map[string][]byte) error) error { - abciEventHistory, ok := ctx.EventManager().(sdk.EventManagerWithHistoryI) - if !ok { - panic("event manager does not implement EventManagerWithHistoryI") - } - - for _, event := range abciEventHistory.GetABCIEventHistory() { - event := event - - // Event type must match the criteria - // nil criteria is considered to match everything - if criteria != nil && !criteria.MatchesEvent(event.Type) { - continue - } - - // Convert the attributes into a map - attributes := make(map[string][]byte) - for _, attribute := range event.Attributes { - attributes[string(attribute.Key)] = []byte(attribute.Value) - } - - valid := true - if criteria != nil { - // Ensure each attribute matches the required criteria - // If a single attribute does not match then we don't continue with the event - eventCriteria := criteria.Events[event.Type] - for key := range eventCriteria.Attributes { - valid = eventCriteria.MatchesAttribute(key, attributes[key]) - if !valid { - break - } - } - } - if !valid { - continue - } - - err := action(event.Type, &attributes) - if err != nil { - return err - } - } - return nil -} - -// FindQualifyingActions iterates event history and applies the RewardAction to them, adds them to the result if they qualify. -func (k Keeper) FindQualifyingActions(ctx sdk.Context, action types.RewardAction) ([]types.EvaluationResult, error) { - result := ([]types.EvaluationResult)(nil) - builder := action.GetBuilder() - - err := k.IterateABCIEvents(ctx, builder.GetEventCriteria(), func(eventType string, attributes *map[string][]byte) error { - // Add the event to the builder - err := builder.AddEvent(eventType, attributes) - if err != nil { - return err - } - - // Not finished building skip attempting to build - if !builder.CanBuild() { - return nil - } - - // Attempt to build - action, err := builder.BuildAction() - if err != nil { - return err - } - result = append(result, action) - - builder.Reset() - - return nil - }) - if err != nil { - return nil, err - } - - return result, nil -} - -// GetAccountKeeper gets this Keeper's AccountKeeper. -func (k Keeper) GetAccountKeeper() types.AccountKeeper { - return k.authkeeper -} - -// GetStakingKeeper gets this Keeper's StakingKeeper. -func (k Keeper) GetStakingKeeper() types.StakingKeeper { - return k.stakingKeeper -} - -// SetStakingKeeper only used in tests -func (k *Keeper) SetStakingKeeper(newKeeper types.StakingKeeper) { - k.stakingKeeper = newKeeper -} - -// SetAccountKeeper only used in tests -func (k *Keeper) SetAccountKeeper(newKeeper authkeeper.AccountKeeper) { - k.authkeeper = newKeeper -} diff --git a/x/reward/keeper/rules_test.go b/x/reward/keeper/rules_test.go deleted file mode 100644 index a4fa5381af..0000000000 --- a/x/reward/keeper/rules_test.go +++ /dev/null @@ -1,1513 +0,0 @@ -package keeper_test - -import ( - "context" - "errors" - "fmt" - "time" - - sdkmath "cosmossdk.io/math" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - teststaking "github.com/cosmos/cosmos-sdk/x/staking/testutil" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - simapp "github.com/provenance-io/provenance/app" - "github.com/provenance-io/provenance/x/reward/types" -) - -const delegatorAddr = "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h" - -var ( - testValidators = []stakingtypes.Validator{} -) - -func setupEventHistory(s *KeeperTestSuite) { - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("key1", "value1"), - sdk.NewAttribute("key2", "value2"), - sdk.NewAttribute("key3", "value3"), - } - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("key1", "value1"), - sdk.NewAttribute("key3", "value2"), - sdk.NewAttribute("key4", "value3"), - } - event1 := sdk.NewEvent("event1", attributes1...) - event2 := sdk.NewEvent("event2", attributes2...) - event3 := sdk.NewEvent("event1", attributes1...) - loggedEvents := sdk.Events{ - event1, - event2, - event3, - } - SetupEventHistory(s, loggedEvents) -} - -func SetupEventHistory(s *KeeperTestSuite, events sdk.Events) { - eventManagerStub := sdk.NewEventManagerWithHistory(events.ToABCIEvents()) - s.ctx = s.ctx.WithEventManager(eventManagerStub) -} - -// with delegate -func SetupEventHistoryWithDelegates(s *KeeperTestSuite) { - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("validator", "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun"), - sdk.NewAttribute("amount", "1000000000000000nhash"), - } - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("module", "staking"), - sdk.NewAttribute("sender", delegatorAddr), - } - attributes3 := []sdk.Attribute{ - sdk.NewAttribute("validator", "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun"), - sdk.NewAttribute("amount", "50000000000000nhash"), - sdk.NewAttribute("new_shares", "50000000000000.000000000000000000"), - } - event1 := sdk.NewEvent("create_validator", attributes1...) - event2 := sdk.NewEvent("message", attributes2...) - event3 := sdk.NewEvent("delegate", attributes3...) - event4 := sdk.NewEvent("message", attributes2...) - loggedEvents := sdk.Events{ - event1, - event2, - event3, - event4, - } - SetupEventHistory(s, loggedEvents) -} - -func (s *KeeperTestSuite) TestProcessTransactions() { - now := time.Unix(1681964400, 0) // roughly Provenance Blockchain's 2-year anniversary. - curHeader := s.ctx.BlockHeader() - curHeader.Time = now - s.ctx = s.ctx.WithBlockHeader(curHeader) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - - // Create a reward program - s.Require().NoError(testutil.FundAccount(s.ctx, s.app.BankKeeper, s.accountAddr, - sdk.NewCoins(sdk.NewInt64Coin("nhash", 1000000))), "funding accountAddr") - minDel := sdk.NewInt64Coin("nhash", 1) - maxDel := sdk.NewInt64Coin("nhash", 40) - program := types.NewRewardProgram( - "title", - "description", - 1, - s.accountAddr.String(), - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - now.Add(1*time.Second), - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDel, - MaximumDelegationAmount: &maxDel, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - s.Require().NoError(s.app.RewardKeeper.CreateRewardProgram(s.ctx, program), "CreateRewardProgram") - - // Start the reward program. - curHeader = s.ctx.BlockHeader() - curHeader.Time = program.ProgramStartTime.Add(1 * time.Second) - s.ctx = s.ctx.WithBlockHeader(curHeader) - s.app.RewardKeeper.UpdateUnexpiredRewardsProgram(s.ctx) - - // make sure the reward program has started. - var err error - program, err = s.app.RewardKeeper.GetRewardProgram(s.ctx, program.GetId()) - s.Require().NoError(err, "GetRewardProgram") - s.Require().Equal(types.RewardProgram_STATE_STARTED.String(), program.State.String(), "program state") - - // Fake some events and process them. - SetupEventHistoryWithDelegates(s) - s.Require().NotPanics(func() { - s.app.RewardKeeper.ProcessTransactions(s.ctx) - }, "ProcessTransactions") - - // Make sure the share was recorded in both the reward account state for the delegator and claim period distribution. - state, err := s.app.RewardKeeper.GetRewardAccountState(s.ctx, program.GetId(), program.GetCurrentClaimPeriod(), delegatorAddr) - s.Require().NoError(err, "GetRewardAccountState") - s.Assert().Equal(1, int(state.SharesEarned), "state.SharesEarned") - claimPeriodRewardDistribution, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, program.GetCurrentClaimPeriod(), program.GetId()) - s.Require().NoError(err, "GetClaimPeriodRewardDistribution") - s.Assert().Equal(1, int(claimPeriodRewardDistribution.GetTotalShares()), "claimPeriodRewardDistribution.GetTotalShares()") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsWildcard() { - setupEventHistory(s) - var events []types.ABCIEvent - criteria := types.NewEventCriteria(events) - counter := 0 - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(3, counter, "should iterate for each event") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsByEventType() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(2, counter, "should iterate only for specified event types") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsByEventTypeAndAttributeName() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - Attributes: map[string][]byte{ - "key1": nil, - }, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(2, counter, "should iterate only for specified event types with matching attributes") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsByEventTypeAndAttributeNameAndValue() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - Attributes: map[string][]byte{ - "key1": []byte("value1"), - }, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(2, counter, "should iterate only for specified event types with matching attributes") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsNonExistantEventType() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event5", - Attributes: map[string][]byte{}, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(0, counter, "should not iterate if event doesn't exist") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsNonExistantAttributeName() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - Attributes: map[string][]byte{ - "blah": []byte("value5"), - }, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(0, counter, "should not iterate if attribute doesn't match") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsNonAttributeValueMatch() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - Attributes: map[string][]byte{ - "key1": []byte("value5"), - }, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return nil - }) - s.Assert().NoError(err, "IterateABCIEvents") - s.Assert().Equal(0, counter, "should not iterate if attribute doesn't match") -} - -func (s *KeeperTestSuite) TestIterateABCIEventsHandlesError() { - setupEventHistory(s) - counter := 0 - events := []types.ABCIEvent{ - { - Type: "event1", - Attributes: map[string][]byte{ - "key1": []byte("value1"), - }, - }, - } - criteria := types.NewEventCriteria(events) - err := s.app.RewardKeeper.IterateABCIEvents(s.ctx, criteria, func(name string, attributes *map[string][]byte) error { - counter += 1 - return errors.New("error") - }) - s.Assert().Error(err, "should throw error when internal function errors") -} - -func (s *KeeperTestSuite) TestFindQualifyingActionsWithDelegates() { - SetupEventHistoryWithDelegates(s) - criteria := types.NewEventCriteria([]types.ABCIEvent{ - { - Type: "message", - Attributes: map[string][]byte{ - "module": []byte("staking"), - }, - }, - { - Type: "delegate", - Attributes: map[string][]byte{}, - }, - { - Type: "create_validator", - Attributes: map[string][]byte{}, - }, - }) - - action := MockAction{Criteria: criteria, Builder: &types.DelegateActionBuilder{}} - events, err := s.app.RewardKeeper.FindQualifyingActions(s.ctx, action) - s.Assert().NoError(err, "should throw no error when handling no events") - s.Assert().Equal(2, len(events), "should find the two delegate events") - for _, event := range events { - s.Assert().Equal(event.Shares, int64(1), "shares must be 1") - s.Assert().Equal(event.Delegator.String(), "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", "delegator address must be correct") - s.Assert().Equal(event.Validator.String(), "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun", "validator address must be correct") - } -} - -func (s *KeeperTestSuite) TestFindQualifyingActionsWithoutDelegates() { - criteria := types.NewEventCriteria([]types.ABCIEvent{ - { - Type: "message", - Attributes: map[string][]byte{ - "module": []byte("staking"), - }, - }, - { - Type: "delegate", - Attributes: map[string][]byte{}, - }, - { - Type: "create_validator", - Attributes: map[string][]byte{}, - }, - }) - action := MockAction{Criteria: criteria, Builder: &types.DelegateActionBuilder{}} - events, err := s.app.RewardKeeper.FindQualifyingActions(s.ctx, action) - s.Assert().NoError(err, "should throw no error when handling no events") - s.Assert().Equal(0, len(events), "should have no events when no delegates are made") -} - -// FindQualifyingActions - -type MockAction struct { - PassEvaluate bool - Criteria *types.EventCriteria - Builder types.ActionBuilder -} - -func (m MockAction) ActionType() string { - return "" -} - -func (m MockAction) Evaluate(ctx sdk.Context, provider types.KeeperProvider, state types.RewardAccountState, event types.EvaluationResult) bool { - return m.PassEvaluate -} - -func (m MockAction) GetEventCriteria() *types.EventCriteria { - return m.Criteria -} - -func (m MockAction) GetBuilder() types.ActionBuilder { - return m.Builder -} - -func (m MockAction) PreEvaluate(ctx sdk.Context, provider types.KeeperProvider, state types.RewardAccountState) bool { - return true - // Any action specific thing that we need to do before evaluation -} - -func (m MockAction) PostEvaluate(ctx sdk.Context, provider types.KeeperProvider, state types.RewardAccountState, evaluationResult types.EvaluationResult) (bool, types.EvaluationResult) { - return true, evaluationResult - // Any action specific thing that we need to do after evaluation -} - -func (s *KeeperTestSuite) TestProcessQualifyingActionsWithNoAbciEvents() { - program := types.RewardProgram{Id: 1, CurrentClaimPeriod: 1} - action := MockAction{PassEvaluate: false} - results := s.app.RewardKeeper.ProcessQualifyingActions(s.ctx, &program, action, []types.EvaluationResult{}) - s.Assert().Equal(0, len(results), "should have no results for empty list of abci events") -} - -func (s *KeeperTestSuite) TestProcessQualifyingActionsCreatesState() { - program := types.RewardProgram{Id: 1, CurrentClaimPeriod: 1} - action := MockAction{PassEvaluate: true} - address1, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - s.app.RewardKeeper.ProcessQualifyingActions(s.ctx, &program, action, []types.EvaluationResult{ - { - Address: address1, - }, - }) - state, _ := s.app.RewardKeeper.GetRewardAccountState(s.ctx, program.GetId(), program.GetCurrentClaimPeriod(), address1.String()) - s.Assert().Equal(program.GetId(), state.RewardProgramId, "claim period should be created when missing") -} - -func (s *KeeperTestSuite) TestProcessQualifyingActionsWithNoMatchingResults() { - program := types.RewardProgram{Id: 1, CurrentClaimPeriod: 1} - action := MockAction{PassEvaluate: false} - results := s.app.RewardKeeper.ProcessQualifyingActions(s.ctx, &program, action, []types.EvaluationResult{ - {Address: types.MustAccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h")}, - {Address: types.MustAccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h")}, - }) - s.Assert().Equal(0, len(results), "should have empty lists when no results match the evaluation") -} - -func (s *KeeperTestSuite) TestProcessQualifyingActionsWithMatchingResults() { - program := types.RewardProgram{Id: 1, CurrentClaimPeriod: 1} - action := MockAction{PassEvaluate: true} - results := s.app.RewardKeeper.ProcessQualifyingActions(s.ctx, &program, action, []types.EvaluationResult{ - {Address: types.MustAccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h")}, - {Address: types.MustAccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h")}, - }) - s.Assert().Equal(2, len(results), "should have all results that evaluate to true") -} - -func (s *KeeperTestSuite) TestFindQualifyingActionsWithNil() { - results := s.app.RewardKeeper.ProcessQualifyingActions(s.ctx, nil, nil, nil) - s.Assert().Equal(0, len(results), "should handle nil input") -} - -// Test ActionDelegate Evaluate - -type MockKeeperProvider struct { -} - -func (m MockKeeperProvider) GetStakingKeeper() types.StakingKeeper { - return MockStakingKeeper{} -} - -func (m MockKeeperProvider) GetAccountKeeper() types.AccountKeeper { - return MockAccountKeeper{} -} - -type MockStakingKeeper struct { -} - -type MockAccountKeeper struct { -} - -func (m MockAccountKeeper) GetModuleAddress(moduleName string) sdk.AccAddress { - return nil -} - -func (m MockStakingKeeper) GetAllDelegatorDelegations(ctx context.Context, delegator sdk.AccAddress) ([]stakingtypes.Delegation, error) { - if delegator.String() == "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h" || delegator.String() == getOperatorBech32AddressForTestValidator().String() { - return []stakingtypes.Delegation{ - { - DelegatorAddress: "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - ValidatorAddress: "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun", - Shares: sdkmath.LegacyNewDec(3), - }, - }, nil - } - return []stakingtypes.Delegation{ - {}, - }, fmt.Errorf("mock error: delegator %s not found", delegator) -} - -func (m MockStakingKeeper) GetDelegation(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (delegation stakingtypes.Delegation, err error) { - if delAddr.String() != "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h" || valAddr.String() != "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun" { - return stakingtypes.Delegation{}, fmt.Errorf("mock error: delegator %s not found for %s", delAddr, valAddr) - } - - return stakingtypes.Delegation{ - DelegatorAddress: "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - ValidatorAddress: "cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun", - Shares: sdkmath.LegacyNewDec(3), - }, nil -} - -func (m MockStakingKeeper) GetLastValidatorPower(ctx context.Context, operator sdk.ValAddress) (power int64, err error) { - validators, _ := m.GetBondedValidatorsByPower(ctx) - for i, v := range validators { - power := int64(len(validators) - i) - if v.GetOperator() == operator.String() { - return power, nil - } - } - return 0, fmt.Errorf("mock error: operator not found %s", operator) -} - -func (s *KeeperTestSuite) createTestValidators(amount int) { - addrDels := simapp.AddTestAddrsIncremental(s.app, s.ctx, amount, sdkmath.NewInt(10000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) - - bondDenom, err := s.app.StakingKeeper.BondDenom(s.ctx) - s.Require().NoError(err, "app.StakingKeeper.BondDenom(ctx)") - - totalSupply := sdk.NewCoins(sdk.NewInt64Coin(bondDenom, 1_000_000_000)) - notBondedPool := s.app.StakingKeeper.GetNotBondedPool(s.ctx) - s.app.AccountKeeper.SetModuleAccount(s.ctx, notBondedPool) - - s.Require().NoError(testutil.FundModuleAccount(s.ctx, s.app.BankKeeper, notBondedPool.GetName(), totalSupply), - "funding %s with %s", notBondedPool.GetName(), totalSupply) - - var validators []stakingtypes.Validator - for i := 0; i < amount; i++ { - validator := teststaking.NewValidator(s.T(), valAddrs[i], PKs[i]) - tokens := s.app.StakingKeeper.TokensFromConsensusPower(s.ctx, int64(1+amount-i)) - validator, _ = validator.AddTokensFromDel(tokens) - validator = keeper.TestingUpdateValidator(s.app.StakingKeeper, s.ctx, validator, true) - validators = append(validators, validator) - - // Create the delegations - bond := stakingtypes.NewDelegation(addrDels[i].String(), valAddrs[i].String(), sdkmath.LegacyNewDec(int64((i+1)*10))) - err = s.app.StakingKeeper.SetDelegation(s.ctx, bond) - s.Require().NoError(err, "SetDelegation %s", bond) - - // We want even validators to be bonded - if i%2 == 0 { - _, err := s.app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(s.ctx) - s.Require().NoError(err, "ApplyAndReturnValidatorSetUpdates") - validator.ABCIValidatorUpdate(s.app.StakingKeeper.PowerReduction(s.ctx)) - } - } - - _, err = s.app.StakingKeeper.EndBlocker(s.ctx) - s.Require().NoError(err, "staking end blocker") - - testValidators = validators -} - -func getTestValidators(start, end int) []stakingtypes.Validator { - var validators []stakingtypes.Validator - for i := start; i <= end; i++ { - validators = append(validators, testValidators[i]) - } - return validators -} - -func (m MockStakingKeeper) GetBondedValidatorsByPower(ctx context.Context) ([]stakingtypes.Validator, error) { - validatorAddress, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - validator, _ := stakingtypes.NewValidator( - validatorAddress.String(), - PKs[100], - stakingtypes.Description{}, - ) - validators := getTestValidators(1, 3) - validators = append(validators, validator) - validators = append(validators, getTestValidators(4, 6)...) - - return validators, nil -} - -func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { - validators := getTestValidators(0, 9) - addrStr := addr.String() - - for _, v := range validators { - if addrStr == v.GetOperator() { - return v, nil - } - } - - return stakingtypes.Validator{}, fmt.Errorf("mock error: validator not fount %s", addr) -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluatePasses() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - - minDelegation := sdk.NewInt64Coin("nhash", 2) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(4, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().True(passed, "evaluate should pass when criteria are met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMinimumActionsNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 2) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(5, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when minimum actions not met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMaximumActionsNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 2) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(5, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when maximum actions not met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMaximumDelegationAmountNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 1) - maxDelegation := sdk.NewInt64Coin("nhash", 1) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(5, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when maximum delegation amount not met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMinimumDelegationAmountNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 5) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(5, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when minimum delegation amount not met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMinimumActiveStakePercentileNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 1) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(11, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when minimum delegation percentage not met") -} - -func (s *KeeperTestSuite) TestActionDelegateEvaluateFailsWhenMaximumDelegationPercentageNotMet() { - action := types.NewActionDelegate() - action.MinimumActions = 1 - action.MaximumActions = 2 - minDelegation := sdk.NewInt64Coin("nhash", 1) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - action.MinimumDelegationAmount = &minDelegation - action.MaximumDelegationAmount = &maxDelegation - action.MinimumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(5, 1) - action.MaximumActiveStakePercentile = sdkmath.LegacyNewDecWithPrec(1, 0) - - keeperProvider := MockKeeperProvider{} - state := types.NewRewardAccountState(0, 0, "", 0, []*types.ActionCounter{}) - state.ActionCounter = types.IncrementActionCount(state.ActionCounter, action.ActionType()) - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - event := types.EvaluationResult{ - Validator: validator, - Delegator: delegator, - } - - passed := action.Evaluate(s.ctx, keeperProvider, state, event) - s.Assert().False(passed, "test should fail when maximum delegation percentage not met") -} - -// Test GetRewardAction - -func (s *KeeperTestSuite) TestGetRewardActionHandlesUnsupportedActions() { - qa := types.QualifyingAction{} - _, err := qa.GetRewardAction(s.ctx) - s.Assert().Error(err, "should throw error when an action is not supported") -} - -func (s *KeeperTestSuite) TestGetRewardActionHandlesActionDelegate() { - delegate := types.QualifyingAction_Delegate{} - qa := types.QualifyingAction{ - Type: &delegate, - } - action, err := qa.GetRewardAction(s.ctx) - s.Assert().NoError(err, "should not throw error when action is supported") - s.Assert().Equal(types.ActionTypeDelegate, action.ActionType(), "should return the correct action type") -} - -// Test DetectQualifyingActions -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1QualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(1, len(qualifyingActions), "must find one qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith2QualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 4, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 4, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(4, len(qualifyingActions), "must find four qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWithNoQualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{}, - ) - rewardProgram.CurrentClaimPeriod = 1 - - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(0, len(qualifyingActions), "must find no qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWithNoMatchingQualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 1000, - MaximumActions: 1000, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(0, len(qualifyingActions), "must find no qualifying actions") -} - -// Test RewardShares -func (s *KeeperTestSuite) TestRewardSharesSingle() { - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{}, - ) - rewardProgram.CurrentClaimPeriod = 1 - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - results := []types.EvaluationResult{ - { - EventTypeToSearch: "delegate", - AttributeKey: "attribute", - Shares: 1, - Address: delegator, - Validator: validator, - Delegator: delegator, - }, - } - - state := types.NewRewardAccountState(rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String(), 0, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - claimPeriodRewardDistribution := types.NewClaimPeriodRewardDistribution(rewardProgram.GetCurrentClaimPeriod(), - rewardProgram.GetId(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - 0, - false, - ) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, claimPeriodRewardDistribution) - - err := s.app.RewardKeeper.RewardShares(s.ctx, &rewardProgram, results) - - state, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String()) - claimPeriodRewardDistribution, _ = s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, rewardProgram.GetCurrentClaimPeriod(), rewardProgram.GetId()) - s.Assert().NoError(err, "should return no error on success") - s.Assert().Equal(uint64(1), state.GetSharesEarned(), "share amount should increment") - s.Assert().Equal(int64(1), claimPeriodRewardDistribution.GetTotalShares(), "total share amount should increment") - s.Assert().Equal(rewardProgram.GetId(), state.GetRewardProgramId(), "reward program id should match") - s.Assert().Equal(rewardProgram.GetCurrentClaimPeriod(), state.GetClaimPeriodId(), "reward claim period id should match") - s.Assert().Equal(delegator.String(), state.GetAddress(), "address should match delegator") -} - -func (s *KeeperTestSuite) TestRewardSharesInvalidClaimPeriodRewardDistribution() { - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{}, - ) - rewardProgram.CurrentClaimPeriod = 1 - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - results := []types.EvaluationResult{ - { - EventTypeToSearch: "delegate", - AttributeKey: "attribute", - Shares: 1, - Address: delegator, - Validator: validator, - Delegator: delegator, - }, - } - - state := types.NewRewardAccountState(rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String(), 0, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - claimPeriodRewardDistribution := types.NewClaimPeriodRewardDistribution(rewardProgram.GetCurrentClaimPeriod(), - rewardProgram.GetId(), - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 0), - 0, - false, - ) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, claimPeriodRewardDistribution) - - err := s.app.RewardKeeper.RewardShares(s.ctx, &rewardProgram, results) - s.Assert().Error(err, "should return an error on invalid claim period reward distribution") -} - -func (s *KeeperTestSuite) TestRewardSharesMultiple() { - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{}, - ) - rewardProgram.CurrentClaimPeriod = 1 - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - results := []types.EvaluationResult{ - { - EventTypeToSearch: "delegate", - AttributeKey: "attribute", - Shares: 1, - Address: delegator, - Validator: validator, - Delegator: delegator, - }, - { - EventTypeToSearch: "delegate", - AttributeKey: "attribute", - Shares: 1, - Address: delegator, - Validator: validator, - Delegator: delegator, - }, - } - - state := types.NewRewardAccountState(rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String(), 0, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state) - claimPeriodRewardDistribution := types.NewClaimPeriodRewardDistribution(rewardProgram.GetCurrentClaimPeriod(), - rewardProgram.GetId(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - 0, - false, - ) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, claimPeriodRewardDistribution) - - err := s.app.RewardKeeper.RewardShares(s.ctx, &rewardProgram, results) - - claimPeriodRewardDistribution, _ = s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, rewardProgram.GetCurrentClaimPeriod(), rewardProgram.GetId()) - state, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String()) - s.Assert().NoError(err, "should return no error on success") - s.Assert().Equal(uint64(2), state.GetSharesEarned(), "share amount should increment") - s.Assert().Equal(int64(2), claimPeriodRewardDistribution.GetTotalShares(), "total share amount should increment") - s.Assert().Equal(rewardProgram.GetId(), state.GetRewardProgramId(), "reward program id should match") - s.Assert().Equal(rewardProgram.GetCurrentClaimPeriod(), state.GetClaimPeriodId(), "reward claim period id should match") - s.Assert().Equal(delegator.String(), state.GetAddress(), "address should match delegator") -} - -func (s *KeeperTestSuite) TestRewardSharesInvalidRewardProgram() { - rewardProgram := types.NewRewardProgram( - "title", - "description", - 0, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{}, - ) - rewardProgram.CurrentClaimPeriod = 1 - - validator, _ := sdk.ValAddressFromBech32("cosmosvaloper15ky9du8a2wlstz6fpx3p4mqpjyrm5cgqh6tjun") - delegator, _ := sdk.AccAddressFromBech32("cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - results := []types.EvaluationResult{ - { - EventTypeToSearch: "delegate", - AttributeKey: "attribute", - Shares: 1, - Address: delegator, - Validator: validator, - Delegator: delegator, - }, - } - claimPeriodRewardDistribution := types.NewClaimPeriodRewardDistribution(rewardProgram.GetCurrentClaimPeriod(), - rewardProgram.GetId(), - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - 0, - false, - ) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, claimPeriodRewardDistribution) - - err := s.app.RewardKeeper.RewardShares(s.ctx, nil, results) - state, _ := s.app.RewardKeeper.GetRewardAccountState(s.ctx, rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod(), delegator.String()) - - s.Assert().Error(err, "should return an error on invalid program") - s.Assert().Equal(uint64(0), state.GetSharesEarned(), "share amount should increment") -} - -// with transfer -func SetupEventHistoryWithTransfers(s *KeeperTestSuite) { - sender := "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h" - recipient := "cosmos1tnh2q55v8wyygtt9srz5safamzdengsnqeycj3" - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("sender", sender), - sdk.NewAttribute("recipient", recipient), - sdk.NewAttribute("amount", "1000000000000000nhash"), - } - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("module", "bank"), - sdk.NewAttribute("sender", sender), - sdk.NewAttribute("action", "/cosmos.bank.v1beta1.MsgSend"), - } - - event1 := sdk.NewEvent("transfer", attributes1...) - event2 := sdk.NewEvent("message", attributes2...) - loggedEvents := sdk.Events{ - event1, - event2, - } - SetupEventHistory(s, loggedEvents) -} - -// with vote -func SetupEventHistoryWithVotes(s *KeeperTestSuite, sender string) { - attributes1 := []sdk.Attribute{ - sdk.NewAttribute("action", "/cosmos.gov.v1beta1.MsgVote"), - } - attributes2 := []sdk.Attribute{ - sdk.NewAttribute("option", "{\"option\":1,\"weight\":\"1.000000000000000000\"}"), - sdk.NewAttribute("proposal_id", "1"), - } - attributes3 := []sdk.Attribute{ - sdk.NewAttribute("module", "governance"), - sdk.NewAttribute("sender", sender), - } - - event1 := sdk.NewEvent("message", attributes1...) - event2 := sdk.NewEvent("proposal_vote", attributes2...) - event3 := sdk.NewEvent("message", attributes3...) - loggedEvents := sdk.Events{ - event1, - event2, - event3, - } - newEvents := loggedEvents.ToABCIEvents() - eventManager, _ := s.ctx.EventManager().(sdk.EventManagerWithHistoryI) - - newEvents = append(newEvents, eventManager.GetABCIEventHistory()...) - eventManagerStub := sdk.NewEventManagerWithHistory(newEvents) - s.ctx = s.ctx.WithEventManager(eventManagerStub) -} - -// transfer -func (s *KeeperTestSuite) TestFindQualifyingActionsWithTransfers() { - SetupEventHistoryWithTransfers(s) - criteria := types.NewEventCriteria([]types.ABCIEvent{ - { - Type: banktypes.EventTypeTransfer, - Attributes: map[string][]byte{}, - }, - }) - - action := MockAction{Criteria: criteria, Builder: &types.TransferActionBuilder{}} - events, err := s.app.RewardKeeper.FindQualifyingActions(s.ctx, action) - s.Assert().NoError(err, "should throw no error when handling no events") - s.Assert().Equal(1, len(events), "should find the one transfer event") - for _, event := range events { - s.Assert().Equal(event.Shares, int64(1), "shares must be 1") - s.Assert().Equal(event.Address.String(), "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", "address must be correct") - } -} - -// vote -func (s *KeeperTestSuite) TestFindQualifyingActionsWithVotes() { - SetupEventHistoryWithVotes(s, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - criteria := types.NewEventCriteria([]types.ABCIEvent{ - { - Type: sdk.EventTypeMessage, - Attributes: map[string][]byte{sdk.AttributeKeyModule: []byte("governance")}, // TODO[1760]: reward: Ensure "governance" is still correct here. - }, - }) - - action := MockAction{Criteria: criteria, Builder: &types.VoteActionBuilder{}} - events, err := s.app.RewardKeeper.FindQualifyingActions(s.ctx, action) - s.Assert().NoError(err, "should throw no error when handling no events") - s.Assert().Equal(1, len(events), "should find the one transfer event") - for _, event := range events { - s.Assert().Equal(event.Shares, int64(1), "shares must be 1") - s.Assert().Equal(event.Address.String(), "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", "address must be correct") - } -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingQualifyingAction() { - SetupEventHistoryWithVotes(s, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 3) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(1, len(qualifyingActions), "must find one qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingQualifyingActionMultiplierPresent() { - s.SetupTest() - SetupEventHistoryWithVotes(s, getOperatorBech32AddressForTestValidator().String()) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - ValidatorMultiplier: 10, - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(1, len(qualifyingActions), "must find one qualifying actions") - s.Assert().Equal(int64(10), qualifyingActions[0].Shares, "shares should be 10") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingQualifyingActionMultiplierPresentAndDelegationRequired() { - s.SetupTest() - SetupEventHistoryWithVotes(s, getOperatorBech32AddressForTestValidator().String()) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 3) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - ValidatorMultiplier: 10, - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(1, len(qualifyingActions), "must find one qualifying actions") - s.Assert().Equal(int64(10), qualifyingActions[0].Shares, "shares should be 10") -} - -func getOperatorBech32AddressForTestValidator() sdk.AccAddress { - validatorAddress, _ := sdk.ValAddressFromBech32(getTestValidators(0, 1)[0].OperatorAddress) - bz, err := sdk.GetFromBech32(validatorAddress.String(), sdk.GetConfig().GetBech32ValidatorAddrPrefix()) - if err != nil { - panic(err) - } - accountAddr := sdk.AccAddress(bz) - return accountAddr -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingQualifyingActionDelegationNotMet() { - SetupEventHistoryWithVotes(s, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 4) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(0, len(qualifyingActions), "must find zero qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingNoQualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(0, len(qualifyingActions), "must find one qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1VotingDelegateQualifyingAction() { - SetupEventHistoryWithDelegates(s) - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(1, len(qualifyingActions), "must find one qualifying actions") -} - -func (s *KeeperTestSuite) TestDetectQualifyingActionsWith1Voting1DelegateQualifyingAction() { - SetupEventHistoryWithDelegates(s) - SetupEventHistoryWithVotes(s, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - s.app.RewardKeeper.SetStakingKeeper(MockStakingKeeper{}) - minDelegation := sdk.NewInt64Coin("nhash", 0) - maxDelegation := sdk.NewInt64Coin("nhash", 10) - - rewardProgram := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", - sdk.NewInt64Coin("hotdog", 10000), - sdk.NewInt64Coin("hotdog", 10000), - time.Now(), - 5, - 5, - 0, - 0, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - { - Type: &types.QualifyingAction_Delegate{ - Delegate: &types.ActionDelegate{ - MinimumActions: 0, - MaximumActions: 1, - MinimumDelegationAmount: &minDelegation, - MaximumDelegationAmount: &maxDelegation, - MinimumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(0, 0), - MaximumActiveStakePercentile: sdkmath.LegacyNewDecWithPrec(1, 0), - }, - }, - }, - }, - ) - rewardProgram.CurrentClaimPeriod = 1 - qualifyingActions, err := s.app.RewardKeeper.DetectQualifyingActions(s.ctx, &rewardProgram) - s.Assert().NoError(err, "must not error") - s.Assert().Equal(2, len(qualifyingActions), "must find one qualifying actions") -} - -func (s *KeeperTestSuite) TestGetAccountKeeper() { - s.Assert().NotNil(s.app.RewardKeeper.GetAccountKeeper()) -} diff --git a/x/reward/keeper/time_management.go b/x/reward/keeper/time_management.go deleted file mode 100644 index 7cdfb91095..0000000000 --- a/x/reward/keeper/time_management.go +++ /dev/null @@ -1,263 +0,0 @@ -package keeper - -import ( - "fmt" - "time" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// UpdateUnexpiredRewardsProgram called from begin blocker, starts/ends or expires rewards programs. -func (k Keeper) UpdateUnexpiredRewardsProgram(origCtx sdk.Context) { - rewardPrograms, err := k.GetAllUnexpiredRewardPrograms(origCtx) - if err != nil { - origCtx.Logger().Error(fmt.Sprintf("error iterating reward programs: %v ", err)) - // called from the beginblocker, not much we can do here but return - return - } - - for index := range rewardPrograms { - // Because this is designed for the BeginBlocker, we don't always have auto-rollback. - // We don't partial state recorded if an error is encountered in the middle. - // So use a cache context and only write it if there wasn't an error. - ctx, writeCtx := origCtx.CacheContext() - switch { - case rewardPrograms[index].IsStarting(ctx): - err = k.StartRewardProgram(ctx, &rewardPrograms[index]) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("cannot start program because of error %v ", err)) - continue - } - case rewardPrograms[index].IsEndingClaimPeriod(ctx): - err = k.EndRewardProgramClaimPeriod(ctx, &rewardPrograms[index]) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("cannot end reward program claim period because of error %v ", err)) - continue - } - case rewardPrograms[index].IsExpiring(ctx): - err = k.ExpireRewardProgram(ctx, &rewardPrograms[index]) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("cannot expire reward program because of error %v ", err)) - continue - } - } - k.SetRewardProgram(ctx, rewardPrograms[index]) - writeCtx() - } -} - -// StartRewardProgram transition reward program to STARTED -func (k Keeper) StartRewardProgram(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - if rewardProgram == nil { - ctx.Logger().Error("Attempting to start nil reward program") - return fmt.Errorf("unable to start nil reward program") - } - - if rewardProgram.GetTotalRewardPool().IsZero() { - ctx.Logger().Error("Attempting to start reward program with no balance") - return fmt.Errorf("unable to start reward program with no balance") - } - - ctx.Logger().Info(fmt.Sprintf("Starting reward program: %v ", rewardProgram)) - rewardProgram.State = types.RewardProgram_STATE_STARTED - err := k.StartRewardProgramClaimPeriod(ctx, rewardProgram) - if err != nil { - return err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewardProgramStarted, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", rewardProgram.GetId())), - ), - ) - - return nil -} - -// StartRewardProgramClaimPeriod Start ClaimPeriod on a given reward program. -func (k Keeper) StartRewardProgramClaimPeriod(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - if rewardProgram == nil { - ctx.Logger().Error("Attempting to start reward program claim for nil reward program") - return fmt.Errorf("unable to start reward program claim for nil reward program") - } - - if rewardProgram.GetClaimPeriods() == 0 { - ctx.Logger().Error("Attempting to start reward program claim with non positive claim periods") - return fmt.Errorf("claim periods must be positive") - } - - blockTime := ctx.BlockTime() - rewardProgram.ClaimPeriodEndTime = blockTime.Add(time.Duration(rewardProgram.ClaimPeriodSeconds) * time.Second) - rewardProgram.CurrentClaimPeriod++ - if rewardProgram.CurrentClaimPeriod > rewardProgram.ClaimPeriods { - rewardProgram.ExpectedProgramEndTime = rewardProgram.ExpectedProgramEndTime.Add(time.Duration(rewardProgram.ClaimPeriodSeconds) * time.Second) - } - - // Get the Claim Period Reward. It should not exceed program balance - claimPeriodAmount := rewardProgram.GetTotalRewardPool().Amount.Quo(sdkmath.NewInt(int64(rewardProgram.GetClaimPeriods()))) - claimPeriodPool := sdk.NewCoin(rewardProgram.GetTotalRewardPool().Denom, claimPeriodAmount) - if rewardProgram.RemainingPoolBalance.IsLT(claimPeriodPool) { - claimPeriodPool = rewardProgram.RemainingPoolBalance - } - - claimPeriodReward := types.NewClaimPeriodRewardDistribution( - rewardProgram.GetCurrentClaimPeriod(), - rewardProgram.GetId(), - claimPeriodPool, - sdk.NewInt64Coin(claimPeriodPool.Denom, 0), - 0, - false, - ) - k.SetClaimPeriodRewardDistribution(ctx, claimPeriodReward) - return nil -} - -// EndRewardProgramClaimPeriod end the claim period of a given reward program. -func (k Keeper) EndRewardProgramClaimPeriod(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - ctx.Logger().Info(fmt.Sprintf("BeginBlocker - Claim period end hit for reward program %v ", rewardProgram)) - - if rewardProgram == nil { - ctx.Logger().Error("EndRewardProgramClaimPeriod RewardProgram is nil") - return fmt.Errorf("unable to end reward program claim period for nil reward program") - } - - claimPeriodReward, err := k.GetClaimPeriodRewardDistribution(ctx, rewardProgram.GetCurrentClaimPeriod(), rewardProgram.GetId()) - if err != nil || claimPeriodReward.GetClaimPeriodId() == 0 { - ctx.Logger().Error(fmt.Sprintf("Missing ClaimPeriodRewardDistribution for RewardProgram %d ", rewardProgram.GetId())) - return fmt.Errorf("a ClaimPeriodRewardDistribution does not exist for RewardProgram %d with claim period %d", rewardProgram.GetId(), rewardProgram.GetId()) - } - - totalClaimPeriodRewards, err := k.CalculateRewardClaimPeriodRewards(ctx, rewardProgram.GetMaxRewardByAddress(), claimPeriodReward) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Unable to calculate claim period rewards for RewardProgram %d ", rewardProgram.GetId()), "err", err) - return err - } - - err = k.MakeRewardClaimsClaimableForPeriod(ctx, rewardProgram.GetId(), rewardProgram.GetCurrentClaimPeriod()) - if err != nil { - return err - } - // Update balances - claimPeriodReward.TotalRewardsPoolForClaimPeriod = claimPeriodReward.TotalRewardsPoolForClaimPeriod.Add(totalClaimPeriodRewards) - claimPeriodReward.ClaimPeriodEnded = true - rewardProgram.RemainingPoolBalance = rewardProgram.RemainingPoolBalance.Sub(totalClaimPeriodRewards) - k.SetClaimPeriodRewardDistribution(ctx, claimPeriodReward) - k.SetRewardProgram(ctx, *rewardProgram) - - if rewardProgram.IsEnding(ctx, rewardProgram.RemainingPoolBalance) { - err = k.EndRewardProgram(ctx, rewardProgram) - if err != nil { - return err - } - } else { - err = k.StartRewardProgramClaimPeriod(ctx, rewardProgram) - if err != nil { - return err - } - } - - return nil -} - -// EndRewardProgram ActualProgramEndTime is updated and program ENDED -func (k Keeper) EndRewardProgram(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - if rewardProgram == nil { - ctx.Logger().Error("Attempting to end reward program for nil reward program") - return fmt.Errorf("unable to end reward program for nil reward program") - } - - ctx.Logger().Info(fmt.Sprintf("BeginBlocker - Ending reward program %v ", rewardProgram)) - blockTime := ctx.BlockTime() - rewardProgram.State = types.RewardProgram_STATE_FINISHED - rewardProgram.ActualProgramEndTime = blockTime - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewardProgramFinished, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", rewardProgram.GetId())), - ), - ) - - return nil -} - -// ExpireRewardProgram return unclaimed rewards to reward creator, and expire the reward program. -func (k Keeper) ExpireRewardProgram(ctx sdk.Context, rewardProgram *types.RewardProgram) error { - if rewardProgram == nil { - ctx.Logger().Error("Attempting to expire reward program for nil reward program") - return fmt.Errorf("unable to expire reward program for nil reward program") - } - ctx.Logger().Info(fmt.Sprintf("BeginBlocker - Expiring reward program %v ", rewardProgram)) - - rewardProgram.State = types.RewardProgram_STATE_EXPIRED - err := k.ExpireRewardClaimsForRewardProgram(ctx, rewardProgram.GetId()) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Failed to expire reward claims for reward program. %v", err)) - } - err = k.RefundRewardClaims(ctx, *rewardProgram) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Failed to refund reward claims. %v", err)) - } - err = k.RefundRemainingBalance(ctx, rewardProgram) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Failed to refund remaining balance. %v", err)) - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewardProgramExpired, - sdk.NewAttribute(types.AttributeKeyRewardProgramID, fmt.Sprintf("%d", rewardProgram.GetId())), - ), - ) - - return err -} - -// CalculateRewardClaimPeriodRewards calculate reward accrued for a claim period for each participant. -func (k Keeper) CalculateRewardClaimPeriodRewards(ctx sdk.Context, maxReward sdk.Coin, claimPeriodReward types.ClaimPeriodRewardDistribution) (sum sdk.Coin, err error) { - sum = sdk.NewInt64Coin(claimPeriodReward.GetRewardsPool().Denom, 0) - - if maxReward.Denom != claimPeriodReward.RewardsPool.GetDenom() { - ctx.Logger().Error(fmt.Sprintf("CalculateRewardClaimPeriodRewards denoms don't match %s %s", maxReward.Denom, claimPeriodReward.RewardsPool.GetDenom())) - return sum, fmt.Errorf("ProgramBalance, MaxReward, and ClaimPeriodReward denoms must match") - } - - participants, err := k.GetRewardAccountStatesForClaimPeriod(ctx, claimPeriodReward.GetRewardProgramId(), claimPeriodReward.GetClaimPeriodId()) - if err != nil { - ctx.Logger().Error(fmt.Sprintf("Unable to get shares for reward program %d's claim period %d ", claimPeriodReward.GetRewardProgramId(), claimPeriodReward.GetClaimPeriodId())) - return sum, fmt.Errorf("unable to get reward claim period shares for reward program %d and claim period %d", claimPeriodReward.GetRewardProgramId(), claimPeriodReward.GetClaimPeriodId()) - } - - for _, participant := range participants { - reward := k.CalculateParticipantReward(ctx, int64(participant.GetSharesEarned()), claimPeriodReward.GetTotalShares(), claimPeriodReward.GetRewardsPool(), maxReward) - - sum = sum.Add(reward) - } - - return sum, nil -} - -// CalculateParticipantReward for each address/participant -func (k Keeper) CalculateParticipantReward(_ sdk.Context, shares int64, totalShares int64, claimRewardPool sdk.Coin, maxReward sdk.Coin) sdk.Coin { - numerator := sdkmath.LegacyNewDec(shares) - denom := sdkmath.LegacyNewDec(totalShares) - - percentage := sdkmath.LegacyNewDec(0) - if totalShares > 0 { - percentage = numerator.Quo(denom) - } - - pool := sdkmath.LegacyNewDec(claimRewardPool.Amount.Int64()) - reward := sdk.NewInt64Coin(claimRewardPool.Denom, pool.Mul(percentage).TruncateInt64()) - - if maxReward.IsLT(reward) { - reward = maxReward - } - - return reward -} diff --git a/x/reward/keeper/time_management_test.go b/x/reward/keeper/time_management_test.go deleted file mode 100644 index 5d7b6ebdc9..0000000000 --- a/x/reward/keeper/time_management_test.go +++ /dev/null @@ -1,1011 +0,0 @@ -package keeper_test - -import ( - "fmt" - "time" - - abci "github.com/cometbft/cometbft/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/provenance-io/provenance/x/reward/types" -) - -func (s *KeeperTestSuite) TestStartRewardProgram() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - - s.Assert().Equal(program.State, types.RewardProgram_STATE_STARTED, "reward program should be in started state") - s.Assert().Equal(uint64(1), program.CurrentClaimPeriod, "current claim period should be set to 1") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - - claimPeriodAmount := program.GetTotalRewardPool().Amount.QuoRaw(int64(program.GetClaimPeriods())) - claimPeriodPool := sdk.NewCoin(program.GetTotalRewardPool().Denom, claimPeriodAmount) - reward, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - s.Assert().NoError(err) - s.Assert().Equal(uint64(1), reward.GetRewardProgramId()) - s.Assert().Equal(uint64(1), reward.GetClaimPeriodId()) - s.Assert().Equal(claimPeriodPool, reward.GetRewardsPool()) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward.GetTotalRewardsPoolForClaimPeriod()) - - events := s.ctx.EventManager().ABCIEvents() - newEvent := events[len(events)-1] - s.Assert().Equal("reward_program_started", newEvent.GetType(), "should emit the correct event type") - s.Assert().Equal([]byte("reward_program_id"), newEvent.GetAttributes()[0].GetKey(), "should emit the correct attribute name") - s.Assert().Equal([]byte("1"), newEvent.GetAttributes()[0].GetValue(), "should emit the correct attribute value") -} - -func (s *KeeperTestSuite) TestStartRewardProgramNoBalance() { - currentTime := time.Now() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 0), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - err := s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - s.Assert().Error(err, "an error should be thrown when there is no balance") - s.Assert().Equal(program.State, types.RewardProgram_STATE_PENDING, "reward program should be in pending state") - s.Assert().Equal(uint64(0), program.CurrentClaimPeriod, "current claim period should be set to 0") -} - -func (s *KeeperTestSuite) TestStartNilRewardProgram() { - err := s.app.RewardKeeper.StartRewardProgram(s.ctx, nil) - s.Assert().Error(err, "must throw error for nil case") -} - -func (s *KeeperTestSuite) TestStartRewardProgramClaimPeriodWithNil() { - err := s.app.RewardKeeper.StartRewardProgramClaimPeriod(s.ctx, nil) - s.Assert().Error(err, "should throw error") -} - -func (s *KeeperTestSuite) TestStartRewardProgramClaimPeriodWithNoPeriods() { - currentTime := time.Now() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - currentTime, - 60*60, - 0, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - - err := s.app.RewardKeeper.StartRewardProgramClaimPeriod(s.ctx, &program) - s.Assert().Error(err, "should throw error") -} - -func (s *KeeperTestSuite) TestStartRewardProgramClaimPeriod() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.ExpectedProgramEndTime = s.ctx.BlockTime() - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgramClaimPeriod(s.ctx, &program) - s.Assert().Equal(uint64(1), program.CurrentClaimPeriod, "current claim period should incremented") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - - claimPeriodAmount := program.GetTotalRewardPool().Amount.QuoRaw(int64(program.GetClaimPeriods())) - claimPeriodPool := sdk.NewCoin(program.GetTotalRewardPool().Denom, claimPeriodAmount) - reward, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - s.Assert().NoError(err) - s.Assert().Equal(uint64(1), reward.GetRewardProgramId()) - s.Assert().Equal(uint64(1), reward.GetClaimPeriodId()) - s.Assert().Equal(claimPeriodPool, reward.GetRewardsPool()) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward.GetTotalRewardsPoolForClaimPeriod()) - s.Assert().Equal(s.ctx.BlockTime(), program.ExpectedProgramEndTime, "expected program end time should not be updated.") -} - -func (s *KeeperTestSuite) TestStartRewardProgramClaimPeriodUpdatesExpectedEndTime() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.CurrentClaimPeriod = program.GetClaimPeriods() - program.ExpectedProgramEndTime = s.ctx.BlockTime() - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgramClaimPeriod(s.ctx, &program) - s.Assert().Equal(uint64(4), program.CurrentClaimPeriod, "current claim period should incremented") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - - claimPeriodAmount := program.GetTotalRewardPool().Amount.QuoRaw(int64(program.GetClaimPeriods())) - claimPeriodPool := sdk.NewCoin(program.GetTotalRewardPool().Denom, claimPeriodAmount) - reward, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 4, 1) - s.Assert().NoError(err) - s.Assert().Equal(uint64(1), reward.GetRewardProgramId()) - s.Assert().Equal(uint64(4), reward.GetClaimPeriodId()) - s.Assert().Equal(claimPeriodPool, reward.GetRewardsPool()) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward.GetTotalRewardsPoolForClaimPeriod()) - s.Assert().Equal(s.ctx.BlockTime().Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ExpectedProgramEndTime, "expected program end time should be updated for rollover.") -} - -func (s *KeeperTestSuite) TestStartRewardProgramClaimPeriodDoesNotExceedBalance() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 100), - currentTime, - 60*60, - 4, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 20) - - s.app.RewardKeeper.StartRewardProgramClaimPeriod(s.ctx, &program) - s.Assert().Equal(uint64(1), program.CurrentClaimPeriod, "current claim period should incremented") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - - reward, err := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - s.Assert().NoError(err) - s.Assert().Equal(uint64(1), reward.GetRewardProgramId()) - s.Assert().Equal(uint64(1), reward.GetClaimPeriodId()) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 20), reward.GetRewardsPool()) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward.GetTotalRewardsPoolForClaimPeriod()) -} - -func (s *KeeperTestSuite) TestEndRewardProgram() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - - s.app.RewardKeeper.EndRewardProgram(s.ctx, &program) - - events := s.ctx.EventManager().ABCIEvents() - newEvent := events[len(events)-1] - s.Assert().Equal("reward_program_finished", newEvent.GetType(), "should emit the correct event type") - s.Assert().Equal([]byte("reward_program_id"), newEvent.GetAttributes()[0].GetKey(), "should emit the correct attribute name") - s.Assert().Equal([]byte("1"), newEvent.GetAttributes()[0].GetValue(), "should emit the correct attribute value") - s.Assert().Equal(program.State, types.RewardProgram_STATE_FINISHED, "reward program should be in finished state") - s.Assert().Equal(blockTime, program.ActualProgramEndTime, "actual program end time should be set") -} - -func (s *KeeperTestSuite) TestEndRewardProgramNil() { - err := s.app.RewardKeeper.EndRewardProgram(s.ctx, nil) - s.Assert().Error(err, "should throw an error for nil") -} - -func (s *KeeperTestSuite) TestExpireRewardProgram() { - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - time.Now(), - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - - s.app.RewardKeeper.ExpireRewardProgram(s.ctx, &program) - s.Assert().Equal(program.State, types.RewardProgram_STATE_EXPIRED, "reward program should be in expired state") - events := s.ctx.EventManager().ABCIEvents() - newEvent := events[len(events)-1] - s.Assert().Equal("reward_program_expired", newEvent.GetType(), "should emit the correct event type") - s.Assert().Equal([]byte("reward_program_id"), newEvent.GetAttributes()[0].GetKey(), "should emit the correct attribute name") - s.Assert().Equal([]byte("1"), newEvent.GetAttributes()[0].GetValue(), "should emit the correct attribute value") -} - -func (s *KeeperTestSuite) TestExpireRewardProgramNil() { - err := s.app.RewardKeeper.ExpireRewardProgram(s.ctx, nil) - s.Assert().Error(err, "should throw an error for nil") -} - -func (s *KeeperTestSuite) TestExpireRewardProgramRefunds() { - program := types.NewRewardProgram( - "title", - "description", - 1, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - time.Now(), - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 80000) - program.ClaimedAmount = sdk.NewInt64Coin("nhash", 10000) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.app.RewardKeeper.ExpireRewardProgram(s.ctx, &program) - - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - s.Assert().Equal(beforeBalance.Add(sdk.NewInt64Coin("nhash", 90000)), afterBalance, "account should get remaining balance and claims") - s.Assert().Equal(program.State, types.RewardProgram_STATE_EXPIRED, "reward program should be in expired state") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsNonMatchingDenoms() { - notMatching := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("hotdog", 0), - sdk.NewInt64Coin("hotdog", 0), - 1, - false, - ) - - _, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 0), notMatching) - s.Assert().Error(err, "error should be thrown when claim period reward distribution doesn't match the others") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsNoSharesForPeriod() { - matching := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 0), - 0, - false, - ) - - reward, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 0), matching) - s.Assert().NoError(err, "No error should be thrown when there are no claimed shares") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward, "should be 0 of the input denom") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsEvenDistributionNoRemainder() { - distribution := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 0), - 2, - false, - ) - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - - reward, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 100), distribution) - s.Assert().NoError(err, "should return no error") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 100), reward, "should distribute all the funds") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsEvenDistributionWithRemainder() { - distribution := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 0), - 3, - false, - ) - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(1, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - - reward, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 100), distribution) - s.Assert().NoError(err, "should return no error") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 99), reward, "should distribute all the funds except for the remainder") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsUnevenDistribution() { - distribution := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 0), - 4, - false, - ) - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 2, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - state3 := types.NewRewardAccountState(1, 1, "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state3) - - reward, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 100), distribution) - s.Assert().NoError(err, "should return no error") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 100), reward, "should distribute all the funds") -} - -func (s *KeeperTestSuite) TestCalculateRewardClaimPeriodRewardsUsesMaxReward() { - distribution := types.NewClaimPeriodRewardDistribution( - 1, - 1, - sdk.NewInt64Coin("nhash", 100), - sdk.NewInt64Coin("nhash", 0), - 2, - false, - ) - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - - reward, err := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, sdk.NewInt64Coin("nhash", 20), distribution) - s.Assert().NoError(err, "should return no error") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 40), reward, "should distribute only up to the maximum reward for each participant") -} - -func (s *KeeperTestSuite) TestCalculateParticipantReward() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 1, 2, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 50), reward, "should get correct cut of pool") -} - -func (s *KeeperTestSuite) TestCalculateParticipantRewardLimitsToMaximum() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 1, 2, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 10)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 10), reward, "should get correct cut of pool") -} - -func (s *KeeperTestSuite) TestCalculateParticipantRewardCanHandleZeroTotalShares() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 1, 0, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward, "should have no reward") -} - -func (s *KeeperTestSuite) TestCalculateParticipantRewardCanHandleZeroEarnedShares() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 0, 10, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward, "should have no reward") -} - -func (s *KeeperTestSuite) TestCalculateParticipantRewardCanHandleZeroRewardPool() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 1, 1, sdk.NewInt64Coin("nhash", 0), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 0), reward, "should have no reward") -} - -func (s *KeeperTestSuite) TestCalculateParticipantRewardTruncates() { - reward := s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 1, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 33), reward, "reward should truncate when < .5") - - reward = s.app.RewardKeeper.CalculateParticipantReward(s.ctx, 2, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100)) - s.Assert().Equal(sdk.NewInt64Coin("nhash", 66), reward, "reward should truncate when >= .5") -} - -func (s *KeeperTestSuite) TestEndRewardProgramClaimPeriodHandlesInvalidLookups() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - - program1 := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program2 := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program3 := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 0), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program1.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program2.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program3.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program2.RemainingPoolBalance = program2.GetTotalRewardPool() - program3.RemainingPoolBalance = sdk.NewInt64Coin("jackthecat", 100) - rewardDistribution := types.NewClaimPeriodRewardDistribution(0, 3, sdk.NewInt64Coin("nhash", 100), sdk.NewInt64Coin("nhash", 100), 1, false) - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, rewardDistribution) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program1) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program2) - s.app.RewardKeeper.SetRewardProgram(s.ctx, program3) - - err := s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program1) - s.Assert().Error(err, "an error should be thrown if there is no program balance for the program") - err = s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program2) - s.Assert().Error(err, "an error should be thrown if there is no claim period reward distribution for the program") - err = s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program3) - s.Assert().Error(err, "an error should be thrown if reward claim calculation fails") -} - -func (s *KeeperTestSuite) TestEndRewardProgramClaimPeriodHandlesNilRewardProgram() { - err := s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, nil) - s.Assert().Error(err, "error should be returned for nil reward program") -} - -func (s *KeeperTestSuite) TestRewardProgramClaimPeriodEnd() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 100000), - currentTime, - 60*60, - 2, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - - // Update the distribution to replicate that a share was actually granted. - rewardDistribution, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - rewardDistribution.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, rewardDistribution) - - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - reward, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - - s.Assert().Equal(sdk.NewInt64Coin("nhash", 50000), program.RemainingPoolBalance, "balance should subtract the claim period reward") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 50000), reward.TotalRewardsPoolForClaimPeriod, "total claim should be increased by the amount rewarded") - s.Assert().Equal(program.State, types.RewardProgram_STATE_STARTED, "reward program should be in started state") - s.Assert().Equal(uint64(2), program.CurrentClaimPeriod, "current claim period should be updated") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") -} - -func (s *KeeperTestSuite) TestRewardProgramClaimPeriodEndTransition() { - currentTime := time.Now() - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 100000), - currentTime, - 60*60, - 2, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - state2 := types.NewRewardAccountState(1, 2, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - reward, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - reward.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, reward) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - reward, _ = s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 2, 1) - reward.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, reward) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - s.Assert().Equal(program.State, types.RewardProgram_STATE_FINISHED, "reward program should be in finished state") - s.Assert().Equal(uint64(2), program.CurrentClaimPeriod, "current claim period should not be updated") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - s.Assert().Equal(blockTime, program.ActualProgramEndTime, "claim period end time should be set") -} - -func (s *KeeperTestSuite) TestRewardProgramClaimPeriodEndTransitionExpired() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - // Normally you would need an additional claim period. However, it should end because the expected time is set. - program.ProgramEndTimeMax = currentTime - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - s.Assert().Equal(types.RewardProgram_STATE_FINISHED, program.State, "reward program should be in finished state") - s.Assert().Equal(uint64(2), program.CurrentClaimPeriod, "current claim period should not be updated") - s.Assert().Equal(blockTime.Add(time.Duration(program.ClaimPeriodSeconds)*time.Second), program.ClaimPeriodEndTime, "claim period end time should be set") - s.Assert().Equal(blockTime, program.ActualProgramEndTime, "claim period end time should be set") -} - -func (s *KeeperTestSuite) TestRewardProgramClaimPeriodEndNoBalance() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 0) - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - s.Assert().Equal(types.RewardProgram_STATE_FINISHED, program.State, "reward program should be in finished state") - s.Assert().Equal(uint64(1), program.CurrentClaimPeriod, "current claim period should not be updated") - s.Assert().Equal(program.ClaimPeriodEndTime, program.ClaimPeriodEndTime, "claim period end time should not be updated") - s.Assert().Equal(blockTime, program.ActualProgramEndTime, "actual end time should be set") -} - -func (s *KeeperTestSuite) TestEndRewardProgramClaimPeriodUpdatesClaimStatus() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 400), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - state1 := types.NewRewardAccountState(1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - state2 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state2) - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - reward, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - reward.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, reward) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - state1, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 1, "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27") - state2, _ = s.app.RewardKeeper.GetRewardAccountState(s.ctx, 1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h") - - // Adjusted after ending period - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state1.GetClaimStatus(), "first claim status should be updated to claimable") - s.Assert().Equal(types.RewardAccountState_CLAIM_STATUS_CLAIMABLE, state2.GetClaimStatus(), "second claim status should be updated to claimable") -} - -func (s *KeeperTestSuite) TestEndRewardProgramClaimPeriodUpdatesBalances() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 400), - sdk.NewInt64Coin("nhash", 1000), - currentTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - reward, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - reward.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, reward) - claimAmount, _ := s.app.RewardKeeper.CalculateRewardClaimPeriodRewards(s.ctx, program.GetMaxRewardByAddress(), reward) - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - // Adjusted after ending period - reward, _ = s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - expectedProgramBalance := program.GetTotalRewardPool().Sub(claimAmount) - s.Assert().Equal(claimAmount, reward.GetTotalRewardsPoolForClaimPeriod(), "the reward for the claim period should be added to total reward") - s.Assert().Equal(expectedProgramBalance, program.GetRemainingPoolBalance(), "the reward for the claim period should be subtracted out of the program balance") - s.Assert().Equal(types.RewardProgram_STATE_STARTED, program.State, "reward program should be in started state") - s.Assert().Equal(uint64(2), program.CurrentClaimPeriod, "next iteration should start") - s.Assert().Equal(true, reward.ClaimPeriodEnded, "claim period should be marked as ended") -} - -func (s *KeeperTestSuite) TestEndRewardProgramClaimPeriodHandlesMinimumRolloverAmount() { - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - blockTime := s.ctx.BlockTime() - program := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 1000), - sdk.NewInt64Coin("nhash", 500), - currentTime, - 60*60, - 2, - 0, - 0, - []types.QualifyingAction{}, - ) - program.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 501) - program.RemainingPoolBalance = program.GetTotalRewardPool() - - s.app.RewardKeeper.StartRewardProgram(s.ctx, &program) - - // Create the shares - state1 := types.NewRewardAccountState(1, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - reward, _ := s.app.RewardKeeper.GetClaimPeriodRewardDistribution(s.ctx, 1, 1) - reward.TotalShares = 1 - s.app.RewardKeeper.SetClaimPeriodRewardDistribution(s.ctx, reward) - - // Should end because the balance should be below 501 - s.app.RewardKeeper.EndRewardProgramClaimPeriod(s.ctx, &program) - - s.Assert().Equal(types.RewardProgram_STATE_FINISHED, program.State, "reward program should be in finished state") - s.Assert().Equal(uint64(1), program.CurrentClaimPeriod, "current claim period should not be updated") - s.Assert().Equal(program.ClaimPeriodEndTime, program.ClaimPeriodEndTime, "claim period end time should not be updated") - s.Assert().Equal(blockTime, program.ActualProgramEndTime, "actual end time should be set") - s.Assert().Equal(sdk.NewInt64Coin("nhash", 500), program.GetRemainingPoolBalance(), "balance should be updated") -} - -func (s *KeeperTestSuite) TestUpdate() { - // Reward Program that has not started - currentTime := time.Now() - s.ctx = s.ctx.WithBlockTime(currentTime) - blockTime := s.ctx.BlockTime() - - notStarted := types.NewRewardProgram( - "title", - "description", - 1, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - blockTime.Add(time.Duration(time.Hour)), - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - notStarted.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - notStarted.RemainingPoolBalance = notStarted.GetTotalRewardPool() - - // Reward Program that is starting - starting := types.NewRewardProgram( - "title", - "description", - 2, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - blockTime, - 60*60, - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - starting.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - starting.RemainingPoolBalance = starting.GetTotalRewardPool() - - // Reward Program that is ready to move onto next claim period - nextClaimPeriod := types.NewRewardProgram( - "title", - "description", - 3, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 1000), - blockTime, - uint64(time.Hour), - 3, - 0, - 0, - []types.QualifyingAction{}, - ) - nextClaimPeriod.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - nextClaimPeriod.RemainingPoolBalance = nextClaimPeriod.GetTotalRewardPool() - s.app.RewardKeeper.StartRewardProgram(s.ctx, &nextClaimPeriod) - nextClaimPeriod.ClaimPeriodEndTime = blockTime - - // Reward program that runs out of funds - ending := types.NewRewardProgram( - "title", - "description", - 4, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 100000), - blockTime, - uint64(time.Hour), - 1, - 0, - 0, - []types.QualifyingAction{}, - ) - ending.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - ending.RemainingPoolBalance = sdk.NewInt64Coin("nhash", 0) - state1 := types.NewRewardAccountState(4, 1, "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", 1, []*types.ActionCounter{}) - s.app.RewardKeeper.SetRewardAccountState(s.ctx, state1) - s.app.RewardKeeper.StartRewardProgram(s.ctx, &ending) - ending.ClaimPeriodEndTime = blockTime - - // Reward program that times out - timeout := types.NewRewardProgram( - "title", - "description", - 5, - "insert address", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 100000), - blockTime, - 0, - 1, - 0, - 0, - []types.QualifyingAction{}, - ) - timeout.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - timeout.ClaimPeriodEndTime = blockTime - timeout.ProgramEndTimeMax = blockTime - timeout.RemainingPoolBalance = timeout.GetTotalRewardPool() - s.app.RewardKeeper.StartRewardProgram(s.ctx, &timeout) - - // Reward program that times out - expiring := types.NewRewardProgram( - "title", - "description", - 6, - "cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv", - sdk.NewInt64Coin("nhash", 100000), - sdk.NewInt64Coin("nhash", 100000), - blockTime, - 0, - 1, - 0, - 0, - []types.QualifyingAction{}, - ) - remainingBalance := expiring.GetTotalRewardPool() - expiring.ActualProgramEndTime = blockTime - expiring.MinimumRolloverAmount = sdk.NewInt64Coin("nhash", 1) - expiring.State = types.RewardProgram_STATE_FINISHED - expiring.RemainingPoolBalance = remainingBalance - - s.app.RewardKeeper.SetRewardProgram(s.ctx, notStarted) - s.app.RewardKeeper.SetRewardProgram(s.ctx, starting) - s.app.RewardKeeper.SetRewardProgram(s.ctx, nextClaimPeriod) - s.app.RewardKeeper.SetRewardProgram(s.ctx, ending) - s.app.RewardKeeper.SetRewardProgram(s.ctx, timeout) - s.app.RewardKeeper.SetRewardProgram(s.ctx, expiring) - - addr, _ := sdk.AccAddressFromBech32("cosmos1ffnqn02ft2psvyv4dyr56nnv6plllf9pm2kpmv") - beforeBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - - // We call update - em := sdk.NewEventManager() - s.app.RewardKeeper.UpdateUnexpiredRewardsProgram(s.ctx.WithEventManager(em)) - - afterBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "nhash") - notStarted, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, notStarted.Id) - starting, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, starting.Id) - nextClaimPeriod, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, nextClaimPeriod.Id) - ending, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, ending.Id) - timeout, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, timeout.Id) - expiring, _ = s.app.RewardKeeper.GetRewardProgram(s.ctx, expiring.Id) - - s.Assert().Equal(uint64(0), notStarted.CurrentClaimPeriod, "claim period should be 0 for a program that is not started") - s.Assert().Equal(notStarted.State, types.RewardProgram_STATE_PENDING, "should be in pending state") - - s.Assert().Equal(uint64(1), starting.CurrentClaimPeriod, "claim period should be 1 for a program that just started") - s.Assert().Equal(starting.State, types.RewardProgram_STATE_STARTED, "should be in started state") - - s.Assert().Equal(uint64(2), nextClaimPeriod.CurrentClaimPeriod, "claim period should be 2 for a program that went to next claim period") - s.Assert().Equal(nextClaimPeriod.State, types.RewardProgram_STATE_STARTED, "should be in started state") - - s.Assert().Equal(uint64(1), ending.CurrentClaimPeriod, "claim period should not increment") - s.Assert().Equal(ending.State, types.RewardProgram_STATE_FINISHED, "should be in finished state") - - s.Assert().Equal(uint64(1), timeout.CurrentClaimPeriod, "claim period should not increment") - s.Assert().Equal(timeout.State, types.RewardProgram_STATE_FINISHED, "should be in finished state") - - s.Assert().Equal(expiring.State, types.RewardProgram_STATE_EXPIRED, "should be in expired state") - s.Assert().Equal(beforeBalance.Add(remainingBalance), afterBalance, "balance should be refunded") - - attr := func(key, value string) abci.EventAttribute { - return abci.EventAttribute{ - Key: key, - Value: value, - } - } - newEvent := func(typeName string, attributes ...abci.EventAttribute) sdk.Event { - return sdk.Event{ - Type: typeName, - Attributes: attributes, - } - } - iStr := func(i uint64) string { - return fmt.Sprintf("%d", i) - } - - moduleAddr := authtypes.NewModuleAddress(types.ModuleName).String() - expAddr := expiring.DistributeFromAddress - amount := "100000nhash" - expectedEvents := sdk.Events{ - newEvent("reward_program_started", attr("reward_program_id", iStr(starting.Id))), - newEvent("reward_program_finished", attr("reward_program_id", iStr(ending.Id))), - newEvent("reward_program_finished", attr("reward_program_id", iStr(timeout.Id))), - newEvent("coin_spent", attr("spender", moduleAddr), attr("amount", amount)), - newEvent("coin_received", attr("receiver", expAddr), attr("amount", amount)), - newEvent("transfer", attr("recipient", expAddr), attr("sender", moduleAddr), attr("amount", amount)), - newEvent("message", attr("sender", moduleAddr)), - newEvent("reward_program_expired", attr("reward_program_id", iStr(expiring.Id))), - } - expectedEventsStrs := EventsStrings(expectedEvents) - - actualEvents := em.Events() - actualEventsStrs := EventsStrings(actualEvents) - if !s.Assert().Equal(expectedEventsStrs, actualEventsStrs, "events emitted during UpdateUnexpiredRewardsProgram") { - s.T().Logf("Expected Events:\n%s", expectedEventsStrs) - s.T().Logf("Actual Events:\n%s", actualEventsStrs) - } -} - -// EventsStrings converts the events into a slice of strings; one string per attribute. -// These are handy for comparing actual/expected events using .Equals. The failure messages are easier to understand. -func EventsStrings(events sdk.Events) []string { - rv := []string{} - for i, event := range events { - for j, attr := range event.Attributes { - indexed := "" - if attr.Index { - indexed = " (indexed)" - } - rv = append(rv, fmt.Sprintf("[%d]%s[%d]: %q = %q%s", i, event.Type, j, attr.Key, attr.Value, indexed)) - } - } - return rv -} diff --git a/x/reward/module/module.go b/x/reward/module/module.go deleted file mode 100644 index a427f916f6..0000000000 --- a/x/reward/module/module.go +++ /dev/null @@ -1,193 +0,0 @@ -package reward - -import ( - "context" - "encoding/json" - "math/rand" - - "github.com/gorilla/mux" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/spf13/cobra" - - abci "github.com/cometbft/cometbft/abci/types" - - cerrs "cosmossdk.io/errors" - - sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - cdctypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - - rewardModule "github.com/provenance-io/provenance/x/reward" - cli "github.com/provenance-io/provenance/x/reward/client/cli" - "github.com/provenance-io/provenance/x/reward/keeper" - simulation "github.com/provenance-io/provenance/x/reward/simulation" - "github.com/provenance-io/provenance/x/reward/types" -) - -var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - // TODO[1760]: app-module: Add more assertions for the new types and clean up stuff no longer needed. -) - -// AppModuleBasic defines the basic application module used by the reward module. -type AppModuleBasic struct { - cdc codec.Codec -} - -// Name returns the reward module's name. -func (AppModuleBasic) Name() string { - return types.ModuleName -} - -// RegisterServices registers a gRPC query service to respond to the -// module-specific gRPC queries. -func (am AppModule) RegisterServices(cfg module.Configurator) { - types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) - types.RegisterQueryServer(cfg.QueryServer(), am.keeper) -} - -// RegisterLegacyAminoCodec registers the reward module's types for the given codec. -func (AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino) { -} - -// RegisterInterfaces registers the reward module's interface types -func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { - types.RegisterInterfaces(registry) -} - -// DefaultGenesis returns default genesis state as raw bytes for the reward -// module. -func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - return cdc.MustMarshalJSON(types.DefaultGenesis()) -} - -// ValidateGenesis performs genesis state validation for the reward module. -func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage) error { - var data types.GenesisState - if err := cdc.UnmarshalJSON(bz, &data); err != nil { - return cerrs.Wrapf(err, "failed to unmarshal %q genesis state", types.ModuleName) - } - - return data.Validate() -} - -// RegisterRESTRoutes registers the REST routes for the reward module. -// Deprecated: RegisterRESTRoutes is deprecated. -func (AppModuleBasic) RegisterRESTRoutes(_ sdkclient.Context, _ *mux.Router) {} - -// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the reward module. -func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, mux *runtime.ServeMux) { - if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { - panic(err) - } -} - -// GetQueryCmd returns the cli query commands for the reward module -func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd() -} - -// GetTxCmd returns the transaction commands for the reward module -func (AppModuleBasic) GetTxCmd() *cobra.Command { - return cli.NewTxCmd() -} - -// AppModule implements the sdk.AppModule interface -type AppModule struct { - AppModuleBasic - keeper keeper.Keeper - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper -} - -// NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, accountKeeper authkeeper.AccountKeeper, bankKeeper bankkeeper.Keeper) AppModule { - return AppModule{ - AppModuleBasic: AppModuleBasic{cdc: cdc}, - keeper: keeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - } -} - -// IsOnePerModuleType is a dummy function that satisfies the OnePerModuleType interface (needed by AppModule). -func (AppModule) IsOnePerModuleType() {} - -// IsAppModule is a dummy function that satisfies the AppModule interface. -func (AppModule) IsAppModule() {} - -// GenerateGenesisState creates a randomized GenState of the rewards module. -func (am AppModule) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// ProposalContents returns content functions used to simulate governance proposals. -func (am AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { - // currently no gov proposals exist - return nil -} - -// RandomizedParams returns randomized module parameters for param change proposals. -func (am AppModule) RandomizedParams(_ *rand.Rand) []simtypes.LegacyParamChange { - // currently no module params exist - return nil -} - -// RegisterStoreDecoder registers a func to decode each module's defined types from their corresponding store key -func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) { - sdr[keeper.StoreKey] = simulation.NewDecodeStore(am.cdc) -} - -// WeightedOperations returns simulation operations (i.e msgs) with their respective weight -func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - return simulation.WeightedOperations( - simState.AppParams, simState.Cdc, am.keeper, am.accountKeeper, am.bankKeeper, - ) -} - -// Name returns the reward module's name. -func (AppModule) Name() string { - return types.ModuleName -} - -// RegisterInvariants does nothing, there are no invariants to enforce -func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} - -// InitGenesis performs genesis initialization for the reward module. It returns -// no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState types.GenesisState - cdc.MustUnmarshalJSON(data, &genesisState) - am.keeper.InitGenesis(ctx, &genesisState) - return []abci.ValidatorUpdate{} -} - -// ExportGenesis returns the exported genesis state as raw bytes for the reward -// module. -func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - gs := am.keeper.ExportGenesis(ctx) - return cdc.MustMarshalJSON(gs) -} - -// ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } - -// BeginBlock is the `BeginBlocker` function run at the beginning of each block to -// process rewards module updates. -func (am AppModule) BeginBlock(ctx context.Context) error { - rewardModule.BeginBlocker(sdk.UnwrapSDKContext(ctx), am.keeper) - return nil -} - -// EndBlock The `EndBlocker` abci call is ran at the end of each block. The `EventManager` is monitored -// and `Qualifying Actions` are deduced from newly created events and prior internal state. -func (am AppModule) EndBlock(ctx context.Context) error { - rewardModule.EndBlocker(sdk.UnwrapSDKContext(ctx), am.keeper) - return nil -} diff --git a/x/reward/simulation/decoder.go b/x/reward/simulation/decoder.go deleted file mode 100644 index 5e44828ce9..0000000000 --- a/x/reward/simulation/decoder.go +++ /dev/null @@ -1,44 +0,0 @@ -package simulation - -import ( - "bytes" - "fmt" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/types/kv" - - "github.com/provenance-io/provenance/x/reward/types" -) - -// NewDecodeStore returns a decoder function closure that unmarshalls the KVPair's -// Value -func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { - return func(kvA, kvB kv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.RewardProgramKeyPrefix): - var attribA, attribB types.RewardProgram - - cdc.MustUnmarshal(kvA.Value, &attribA) - cdc.MustUnmarshal(kvB.Value, &attribB) - - return fmt.Sprintf("%v\n%v", attribA, attribB) - - case bytes.Equal(kvA.Key[:1], types.ClaimPeriodRewardDistributionKeyPrefix): - var attribA, attribB types.ClaimPeriodRewardDistribution - - cdc.MustUnmarshal(kvA.Value, &attribA) - cdc.MustUnmarshal(kvB.Value, &attribB) - - return fmt.Sprintf("%v\n%v", attribA, attribB) - case bytes.Equal(kvA.Key[:1], types.AccountStateKeyPrefix): - var attribA, attribB types.RewardAccountState - - cdc.MustUnmarshal(kvA.Value, &attribA) - cdc.MustUnmarshal(kvB.Value, &attribB) - - return fmt.Sprintf("%v\n%v", attribA, attribB) - default: - panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key)) - } - } -} diff --git a/x/reward/simulation/genesis.go b/x/reward/simulation/genesis.go deleted file mode 100644 index 5f56a2bd10..0000000000 --- a/x/reward/simulation/genesis.go +++ /dev/null @@ -1,130 +0,0 @@ -package simulation - -// DONTCOVER - -import ( - "encoding/json" - "fmt" - "math/rand" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - - "github.com/provenance-io/provenance/internal/pioconfig" - "github.com/provenance-io/provenance/x/reward/types" -) - -// Simulation parameter constants -const ( - MaxActions = "max_actions" - MinActions = "min_actions" - TotalRewardsPool = "total_rewards_pool" - MaxRewardByAddress = "max_reward_by_address" -) - -// GenTotalRewardsPool randomized TotalRewardsPool -func GenTotalRewardsPool(r *rand.Rand) sdk.Coin { - return sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, int64(randIntBetween(r, 1000, 10000000000))) -} - -// GenMaxRewardsByAddress randomized MaxRewardByAddress -func GenMaxRewardsByAddress(r *rand.Rand) sdk.Coin { - return sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, int64(randIntBetween(r, 1, 999))) -} - -// MaxActionsFn randomized MaxActions -func MaxActionsFn(r *rand.Rand) uint64 { - return uint64(randIntBetween(r, 100, 100000000)) -} - -// MinActionsFn randomized MinActions -func MinActionsFn(r *rand.Rand) uint64 { - return uint64(r.Intn(101)) -} - -// RandomizedGenState generates a random GenesisState for distribution -func RandomizedGenState(simState *module.SimulationState) { - var totalRewardsPool sdk.Coin - simState.AppParams.GetOrGenerate( - TotalRewardsPool, &totalRewardsPool, simState.Rand, - func(r *rand.Rand) { totalRewardsPool = GenTotalRewardsPool(r) }, - ) - var maxRewardsByAddress sdk.Coin - simState.AppParams.GetOrGenerate( - MaxRewardByAddress, &maxRewardsByAddress, simState.Rand, - func(r *rand.Rand) { maxRewardsByAddress = GenMaxRewardsByAddress(r) }, - ) - - var maxActions uint64 - simState.AppParams.GetOrGenerate( - MaxActions, &maxActions, simState.Rand, - func(r *rand.Rand) { maxActions = MaxActionsFn(r) }, - ) - - var minActions uint64 - simState.AppParams.GetOrGenerate( - MinActions, &minActions, simState.Rand, - func(r *rand.Rand) { minActions = MinActionsFn(r) }, - ) - - minDelegation := sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, int64(minActions)) - - now := simState.GenTimestamp - claimPeriodSeconds := uint64(simState.Rand.Intn(100000)) - claimPeriods := uint64(simState.Rand.Intn(100)) + 1 - maxRolloverPeriods := uint64(simState.Rand.Intn(10)) - expireClaimPeriods := uint64(simState.Rand.Intn(100000)) - expectedProgramEndTime := types.CalculateExpectedEndTime(now, claimPeriodSeconds, claimPeriods) - programEndTimeMax := types.CalculateEndTimeMax(now, claimPeriodSeconds, claimPeriods, maxRolloverPeriods) - rewardProgram := types.RewardProgram{ - Title: "title", - Description: "description", - Id: uint64(simState.Rand.Intn(100000)), - DistributeFromAddress: simState.Accounts[0].Address.String(), - TotalRewardPool: totalRewardsPool, - RemainingPoolBalance: totalRewardsPool, - ClaimedAmount: sdk.NewInt64Coin(totalRewardsPool.Denom, 0), - MaxRewardByAddress: maxRewardsByAddress, - ProgramStartTime: now.UTC(), - ExpectedProgramEndTime: expectedProgramEndTime.UTC(), - ProgramEndTimeMax: programEndTimeMax.UTC(), - ClaimPeriodSeconds: claimPeriodSeconds, - ClaimPeriods: claimPeriods, - MaxRolloverClaimPeriods: maxRolloverPeriods, - ExpirationOffset: expireClaimPeriods, - State: types.RewardProgram_STATE_PENDING, - QualifyingActions: []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: minActions, - MaximumActions: maxActions, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }, - MinimumRolloverAmount: sdk.NewInt64Coin(totalRewardsPool.Denom, 100_000_000_000), - } - - rewards := types.NewGenesisState( - uint64(100001), - []types.RewardProgram{ - rewardProgram, - }, - []types.ClaimPeriodRewardDistribution{}, - []types.RewardAccountState{}, - ) - - bz, err := json.MarshalIndent(&rewards, "", " ") - if err != nil { - panic(err) - } - fmt.Printf("Selected randomly generated reward programs:\n%s\n", bz) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(rewards) -} - -// randIntBetween generates a random number between min and max inclusive. -func randIntBetween(r *rand.Rand, min, max int) int { - return r.Intn(max-min+1) + min -} diff --git a/x/reward/simulation/operations.go b/x/reward/simulation/operations.go deleted file mode 100644 index ce0b1e2db8..0000000000 --- a/x/reward/simulation/operations.go +++ /dev/null @@ -1,205 +0,0 @@ -package simulation - -// DONTCOVER - -import ( - "math/rand" - "time" - - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/simulation" - - simappparams "github.com/provenance-io/provenance/app/params" - "github.com/provenance-io/provenance/internal/pioconfig" - "github.com/provenance-io/provenance/x/reward/keeper" - "github.com/provenance-io/provenance/x/reward/types" -) - -// Simulation operation weights constants -const ( - OpWeightSubmitCreateRewardsProposal = "op_weight_submit_create_rewards_proposal" - OpWeightEndRewardsProposal = "op_weight_submit_end_reward_proposal" -) - -// WeightedOperations returns all the operations from the module with their respective weights -func WeightedOperations( - appParams simtypes.AppParams, cdc codec.JSONCodec, k keeper.Keeper, ak authkeeper.AccountKeeperI, bk bankkeeper.Keeper, -) simulation.WeightedOperations { - var ( - weightMsgAddRewardsProgram int - weightMsgEndRewardProgram int - ) - - appParams.GetOrGenerate(OpWeightSubmitCreateRewardsProposal, &weightMsgAddRewardsProgram, nil, - func(_ *rand.Rand) { - weightMsgAddRewardsProgram = simappparams.DefaultWeightSubmitCreateRewards - }, - ) - appParams.GetOrGenerate(OpWeightEndRewardsProposal, &weightMsgEndRewardProgram, nil, - func(_ *rand.Rand) { - weightMsgEndRewardProgram = simappparams.DefaultWeightSubmitEndRewards - }, - ) - - return simulation.WeightedOperations{ - simulation.NewWeightedOperation( - weightMsgAddRewardsProgram, - SimulateMsgCreateRewardsProgram(k, ak, bk), - ), - simulation.NewWeightedOperation( - weightMsgEndRewardProgram, - SimulateMsgEndRewardsProgram(k, ak, bk), - ), - } -} - -// SimulateMsgCreateRewardsProgram sends of a MsgCreateRewardProgramRequest. -func SimulateMsgCreateRewardsProgram(_ keeper.Keeper, ak authkeeper.AccountKeeperI, bk bankkeeper.Keeper) simtypes.Operation { - return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, - ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - simAccount, _ := simtypes.RandomAcc(r, accs) - var totalRewardsPool = GenTotalRewardsPool(r) - var maxRewardsByAddress = GenMaxRewardsByAddress(r) - var maxActions = MaxActionsFn(r) - - var minActions = MinActionsFn(r) - - minDelegation := sdk.NewInt64Coin(pioconfig.GetProvenanceConfig().BondDenom, int64(minActions)) - - now := time.Now() - claimPeriods := uint64(r.Intn(100)) - maxRolloverPeriods := uint64(r.Intn(10)) - expireClaimPeriods := uint64(r.Intn(100)) - - msg := types.NewMsgCreateRewardProgramRequest("title", - "description", - simAccount.Address.String(), - totalRewardsPool, - maxRewardsByAddress, - now.Add(5*time.Second), - claimPeriods, - 1, - maxRolloverPeriods, - expireClaimPeriods, - []types.QualifyingAction{ - { - Type: &types.QualifyingAction_Vote{ - Vote: &types.ActionVote{ - MinimumActions: minActions, - MaximumActions: maxActions, - MinimumDelegationAmount: minDelegation, - }, - }, - }, - }) - return Dispatch(r, app, ctx, ak, bk, simAccount, chainID, msg, nil) - } -} - -// Dispatch sends an operation to the chain using a given account/funds on account for fees. Failures on the server side -// are handled as no-op msg operations with the error string as the status/response. -func Dispatch( - r *rand.Rand, - app *baseapp.BaseApp, - ctx sdk.Context, - ak authkeeper.AccountKeeperI, - bk bankkeeper.Keeper, - from simtypes.Account, - chainID string, - msg sdk.Msg, - futures []simtypes.FutureOperation, -) ( - simtypes.OperationMsg, - []simtypes.FutureOperation, - error, -) { - account := ak.GetAccount(ctx, from.Address) - spendable := bk.SpendableCoins(ctx, account.GetAddress()) - - fees, err := simtypes.RandomFees(r, ctx, spendable) - if err != nil { - return simtypes.NoOpMsg(sdk.MsgTypeURL(msg), sdk.MsgTypeURL(msg), "unable to generate fees"), nil, err - } - err = testutil.FundAccount(ctx, bk, account.GetAddress(), sdk.NewCoins(sdk.Coin{ - Denom: pioconfig.GetProvenanceConfig().BondDenom, - Amount: sdkmath.NewInt(1_000_000_000_000_000), - })) - if err != nil { - return simtypes.NoOpMsg(sdk.MsgTypeURL(msg), sdk.MsgTypeURL(msg), "unable to fund account"), nil, err - } - txGen := simappparams.MakeTestEncodingConfig().TxConfig - tx, err := simtestutil.GenSignedMockTx( - r, - txGen, - []sdk.Msg{msg}, - fees, - simtestutil.DefaultGenTxGas, - chainID, - []uint64{account.GetAccountNumber()}, - []uint64{account.GetSequence()}, - from.PrivKey, - ) - if err != nil { - return simtypes.NoOpMsg(sdk.MsgTypeURL(msg), sdk.MsgTypeURL(msg), "unable to generate mock tx"), nil, err - } - - _, _, err = app.SimDeliver(txGen.TxEncoder(), tx) - if err != nil { - return simtypes.NoOpMsg(sdk.MsgTypeURL(msg), sdk.MsgTypeURL(msg), err.Error()), nil, nil - } - - return simtypes.NewOperationMsg(msg, true, ""), futures, nil -} - -// SimulateMsgEndRewardsProgram sends a MsgEndRewardProgramRequest for a random existing reward program. -func SimulateMsgEndRewardsProgram(k keeper.Keeper, ak authkeeper.AccountKeeperI, bk bankkeeper.Keeper) simtypes.Operation { - return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, - ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - rewardProgram := randomRewardProgram(r, ctx, k) - if rewardProgram == nil { - return simtypes.NoOpMsg(sdk.MsgTypeURL(&types.MsgEndRewardProgramRequest{}), sdk.MsgTypeURL(&types.MsgEndRewardProgramRequest{}), "unable to find a valid reward program"), nil, nil - } - var simAccount simtypes.Account - var found bool - addr, err := sdk.AccAddressFromBech32(rewardProgram.DistributeFromAddress) - if err != nil { - // should just noit be possible and panic on the test - panic(err) - } - simAccount, found = simtypes.FindAccount(accs, addr) - if !found { - return simtypes.NoOpMsg(sdk.MsgTypeURL(&types.MsgEndRewardProgramRequest{}), sdk.MsgTypeURL(&types.MsgEndRewardProgramRequest{}), "creator of rewards program account does not exist"), nil, nil - } - msg := types.NewMsgEndRewardProgramRequest(rewardProgram.Id, rewardProgram.DistributeFromAddress) - return Dispatch(r, app, ctx, ak, bk, simAccount, chainID, msg, nil) - } -} - -func randomRewardProgram(r *rand.Rand, ctx sdk.Context, k keeper.Keeper) *types.RewardProgram { - var rewardPrograms []types.RewardProgram - err := k.IterateRewardPrograms(ctx, func(rewardProgram types.RewardProgram) (stop bool, err error) { - rewardPrograms = append(rewardPrograms, rewardProgram) - return false, nil - }) - if err != nil { - // sim tests should fail if iterator errors - panic(err) - } - if len(rewardPrograms) == 0 { - return nil - } - idx := r.Intn(len(rewardPrograms)) - return &rewardPrograms[idx] -} diff --git a/x/reward/simulation/operations_test.go b/x/reward/simulation/operations_test.go deleted file mode 100644 index 92d6f0f495..0000000000 --- a/x/reward/simulation/operations_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package simulation_test - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/suite" - - sdk "github.com/cosmos/cosmos-sdk/types" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - - "github.com/provenance-io/provenance/app" - simappparams "github.com/provenance-io/provenance/app/params" - "github.com/provenance-io/provenance/x/reward/simulation" - "github.com/provenance-io/provenance/x/reward/types" -) - -type SimTestSuite struct { - suite.Suite - - ctx sdk.Context - app *app.App -} - -func (s *SimTestSuite) SetupTest() { - s.app = app.Setup(s.T()) - s.ctx = s.app.BaseApp.NewContext(false) -} - -func (s *SimTestSuite) TestWeightedOperations() { - cdc := s.app.AppCodec() - appParams := make(simtypes.AppParams) - - weightedOps := simulation.WeightedOperations(appParams, cdc, s.app.RewardKeeper, - s.app.AccountKeeper, s.app.BankKeeper, - ) - - // setup 3 accounts - source := rand.NewSource(1) - r := rand.New(source) - accs := s.getTestingAccounts(r, 3) - - expected := []struct { - weight int - opMsgRoute string - opMsgName string - }{ - {weight: simappparams.DefaultWeightSubmitCreateRewards, opMsgRoute: types.RouterKey, opMsgName: sdk.MsgTypeURL(&types.MsgCreateRewardProgramRequest{})}, - {weight: simappparams.DefaultWeightSubmitEndRewards, opMsgRoute: types.RouterKey, opMsgName: sdk.MsgTypeURL(&types.MsgEndRewardProgramRequest{})}, - } - - for i, w := range weightedOps { - operationMsg, _, _ := w.Op()(r, s.app.BaseApp, s.ctx, accs, "") - // the following checks are very much dependent from the ordering of the output given - // by WeightedOperations. if the ordering in WeightedOperations changes some tests - // will fail - s.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") - s.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") - s.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") - } -} - -func (s *SimTestSuite) TestSimulateMsgAddRewards() { - - // setup 3 accounts - source := rand.NewSource(1) - r := rand.New(source) - accounts := s.getTestingAccounts(r, 3) - - // execute operation - op := simulation.SimulateMsgCreateRewardsProgram(s.app.RewardKeeper, s.app.AccountKeeper, s.app.BankKeeper) - operationMsg, futureOperations, err := op(r, s.app.BaseApp, s.ctx, accounts, "") - s.Require().NoError(err) - - var msg types.MsgCreateRewardProgramRequest - types.ModuleCdc.Unmarshal(operationMsg.Msg, &msg) - - s.Require().True(operationMsg.OK, operationMsg.String()) - s.Require().Equal(sdk.MsgTypeURL(&msg), operationMsg.Name) - s.Require().Equal(types.RouterKey, operationMsg.Route) - s.Require().Len(futureOperations, 0) -} - -func TestSimTestSuite(t *testing.T) { - suite.Run(t, new(SimTestSuite)) -} - -func (s *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { - accounts := simtypes.RandomAccounts(r, n) - - initAmt := sdk.TokensFromConsensusPower(1000000, sdk.DefaultPowerReduction) - initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) - - // add coins to the accounts - for _, account := range accounts { - acc := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, account.Address) - s.app.AccountKeeper.SetAccount(s.ctx, acc) - err := testutil.FundAccount(s.ctx, s.app.BankKeeper, account.Address, initCoins) - s.Require().NoError(err) - } - - return accounts -} diff --git a/x/reward/spec/01_concepts.md b/x/reward/spec/01_concepts.md deleted file mode 100644 index 6456bf62da..0000000000 --- a/x/reward/spec/01_concepts.md +++ /dev/null @@ -1,35 +0,0 @@ - - -# Concepts - - - - [Reward Program](#reward-program) - - [Qualifying Actions and Eligibility Criteria](#qualifying-actions-and-eligibility-criteria) - - [Claim Period](#claim-period) - - [Reward Claim](#reward-claim) - - [Rollover](#rollover) - - [Refunding](#refunding) - -## Reward Program -Reward Programs are configurable campaigns that encourage users to participate in the Provenance Blockchain. Entities interested in creating a Reward Program will supply their new program with funds, set the duration of their program, and provide the participation requirements. - -## Qualifying Actions and Eligibility Criteria -A `Qualifying Action` is one or more transactions that a user performs on the Provenance Blockchain that has been listed within the `Reward Program`. These actions are then evaluated against a set of criteria that are also defined within the `Reward Program` known as `Eligiblity Criteria`. Users become participants in the Reward Program by performing a `Qualifying Action` and meeting all conditions specified by its `Eligiblity Criteria`. - -## Claim Period -A `Reward Program` is split into one or more time intervals known as `Claim Periods`. Each of these `Claim Periods` gets an equal portion of the `Reward Program Reward Pool` known as the `Claim Period Reward Pool`. Users can participate within these `Claim Periods` and are rewarded for their actions. - -## Reward Claim -When a user participates in a `Reward Program` they are granted one or more shares of the `Claim Period Reward Pool`. Once the`Claim Period` ends, the participant will be able to claim their reward by performing a claim transaction. The participant's reward is proportional to their activity compared to everyone else within a `Claim Period`. Users must claim their rewards before the `Reward Program` expires. Additionally, users will be limited to `max_reward_per_address` of the `Claim Period Reward Pool`. - -**Reward For Claim Period** - -$$\left( ClaimPeriodRewardPool \right) \times \left( EarnedShares \over ClaimPeriodShares \right) $$ - -## Rollover -It is possible that not all of the `Claim Period Reward Pool` will be distributed. This can happen when there is not enough activity, and participants are gated by the `max_reward_per_address`. The `Reward Program` will attempt to move these funds into a `Rollover Claim Period`. A `Rollover Claim Period` behaves exactly like any other `Claim Period`, but it is not guaranteed to have an equal portion of the original `Reward Program Reward Pool`. A `Reward Program` may run up to `max_rollover_claim_periods`, but is not guaranteed to run any of them. This is dependent on user activity, `program_end_time_max` field, and the `minimum_rollover_amount` field. Currently, the `minimum_rollover_amount` is set to 10% of the `Claim Period Reward Pool`. - -## Refunding -When a `Reward Program` ends it gives all participants `expiration_offset` seconds to claim their rewards. After `expiration_offset` seconds the `Reward Program` expires and prevents participants from claiming. The unclaimed rewards and any funds still remaining within the `Reward Program Reward Pool` will be given back to the creator. diff --git a/x/reward/spec/02_state.md b/x/reward/spec/02_state.md deleted file mode 100644 index be3d433fb8..0000000000 --- a/x/reward/spec/02_state.md +++ /dev/null @@ -1,83 +0,0 @@ - - -# State - -The rewards module manages the state of every reward program and each of its participants. - - - - [Reward Program](#reward-program) - - [Claim Period Reward Distribution](#claim-period-reward-distribution) - - [Reward Account State](#reward-account-state) - - [Action Counter](#action-counter) - - [Qualifying Actions](#qualifying-actions) - - [Action Delegate](#action-delegate) - - [Action Transfer](#action-transfer) - - [Action Vote](#action-vote) - ---- -## Reward Program - -A `RewardProgram` is the main data structure used by the Active Participation and Engagement (APE) module. It keeps track of the state, balances, qualifying actions, timers, and counters for a single Reward Program. Every Reward Program gets its own unique identifier that we track within the store. - -* Reward Program: `0x01 | RewardProgram ID (8 bytes) -> ProtocolBuffers(RewardProgram)` -* Reward Program ID: `0x02 -> uint64(RewardProgramID)` - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L12-L73 - ---- -## Claim Period Reward Distribution - -A `ClaimPeriodRewardDistribution` is created for each claim period of every `RewardProgram`. Its purpose is to track live claim period specific information. Examples of this include the total number of granted shares in the claim period, sum of of all its rewards given out as claims, and the amount of reward allocated to it from the `RewardProgram`. - -* Claim Period Reward Distribution: `0x03 | Reward Program ID (8 bytes) | Claim Period ID (8 bytes) -> ProtocolBuffers(ClaimPeriodRewardDistribution)` - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L75-L92 - ---- -## Reward Account State - -The purpose of `RewardAccountState` is to track state at the address level of a claim period. It counts the number of claim period shares the user obtained, the status of their `RewardClaim`, and other stateful information that assists the system in properly granting rewards. - -* AccountStateAddressLookupKeyPrefix: `0x04 | Account Address (n bytes, with the address length being stored in the first byte {int64(address[1:2][0])}) | Reward Program ID (8 bytes) | Claim Period ID (8 bytes) -> ProtocolBuffers(RewardAccountState)` -* AccountStateKeyPrefix: `0x05 | Reward Program ID (8 bytes) | Claim Period ID (8 bytes) | Account Address (n bytes, with the address length being stored in the first byte {int64(address[1:2][0])}) -> ProtocolBuffers(RewardAccountState)` - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L94-L123 - -### Action Counter - -`ActionCounter` tracks the number of times an action has been performed. - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L190-L199 - ---- -## Qualifying Actions - -A list of one or more actions that a user can perform to attempt to participate in a `RewardProgram`. In order to be considered a participant and granted a share then all the `EligiblityCriteria` on the action must be met. Each action has its own `EligiblityCriteria`, which is independently evaluated against system state and `RewardAccountState` for that user. Each `Qualifying Action` is evaluated independently, thus it is possible for a user to earn more than one reward for a single action. - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L125-L141 - -### Action Delegate - -`ActionDelegate` is when a user performs a delegate. - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L143-L162 - -The triggering account must have a total delegation amount within the bands of [`minimum_delegation_amount`,`maximum_delegation_amount`]. Additionally, the validator they are staking to must be within the [`minimum_active_stake_percentile`,`maximum_active_stake_percentile`] power percentile. If both of these criteria are met then the delegate is considered successful. The `minimum_actions` and `maximum_actions` fields are the number of successful delegate that must be performed. Once all these conditions are met then the user will receive a share. - -### Action Transfer - -`ActionTransfer` is when a user transfers coins. - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L164-L175 - -If the triggering account has delegated at least the `minimum_delegation_amount`, then the transfer action will be considered successful. The `minimum_actions` and `maximum_actions` fields are the number of successful transfer that must be performed. When all these conditions are met, then the user will receive a share. - -### Action Vote - -`ActionVote` is when a user votes on a proposal. - -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/reward.proto#L177-L188 - -If the triggering account has delegated at least the `minimum_delegation_amount`, then the vote action will be considered successful. The `minimum_actions` and `maximum_actions` fields are the number of successful votes that must be performed. When all these conditions are met, then the user will receive a share. diff --git a/x/reward/spec/03_state_transitions.md b/x/reward/spec/03_state_transitions.md deleted file mode 100644 index 429bdf1c62..0000000000 --- a/x/reward/spec/03_state_transitions.md +++ /dev/null @@ -1,72 +0,0 @@ - - -# State Transitions - -This document describes the state transition operations involved in the rewards module. - - - - [Reward Programs](#reward-programs) - - [Pending](#pending) - - [Started](#started) - - [Finished](#finished) - - [Expired](#expired) - - [Reward Claims](#reward-claims) - - [Unclaimable](#unclaimable) - - [Claimable](#claimable) - - [Claimed](#claimed) - - [Expired](#expired) - - - -## Reward Programs -State transition for Reward Programs happen on `BeginBlock` and make use of the `BlockTime` attribute. - -A Reward Program can be `Pending`, `Started`, `Finished`, or `Expired`. A Reward Program will move through all these states, and will initially be in the `Pending` state. - -#### Note -A Reward Program creator may end a Reward Program early while it's in `Pending` or `Started` state. A Reward Program in the `Pending` state will be deleted and not progress through all the states. Any program that is ended after it's in the `Started` state will transition to the `Finished` state on the next `BeginBlock`. - -### Pending -Reward program has *not* started. - -#### Note -A user may force a Reward Program in this state to end with the `end-reward-program` transaction. In this case the Reward Program will be deleted and not progress. - -### Started -The Reward Program has started, and users can participate by performing qualifying actions. Participants can claim their rewards at the end of the claim period that the qualifying action was performed in. - -#### Note -A user may force a Reward Program in this state to end with the `end-reward-program` transaction. The Reward Program will transition to the `Finished` state on the next `BeginBlock`. - -### Finished -The Reward Program has ended, and participants can no longer make qualifying actions. Participants have a limited amount of time to collect their remaining rewards. - -### Expired -Reward program has passed its expiration date, and participants can no longer claim rewards. The remaining balance and any unclaimed rewards will be returned to the creator. - -
- -
- -## Reward Claims -State transitions for a Reward Claim happen on `BeginBlock` and on claim transactions. - -A Reward Claim can be `Unclaimable`, `Claimable`, `Claimed`, or `Expired`. A Reward Claim will always start as `Unclaimable` and eventually become `Claimable`. If a participant claims their reward then the Reward Claim will become `Claimed`, otherwise it will timeout and enter the `Expired` state where they can no longer claim it. - -### Unclaimable -The reward has been granted to a participant, but it cannot be claimed until the current claim period ends. - -### Claimable -The reward has been granted to the participant, and it's claimable by the participant via a transaction. If the reward is not claimed it will eventually expire. - -### Claimed -The reward has been granted and received by the participant. A reward cannot be claimed more than once. - -### Expired -The reward has been cleaned up and the participant can no longer claim it. The funds attached to the reward claim are refunded back to the program creator. - -- -
diff --git a/x/reward/spec/04_messages.md b/x/reward/spec/04_messages.md deleted file mode 100644 index 4420e004fd..0000000000 --- a/x/reward/spec/04_messages.md +++ /dev/null @@ -1,79 +0,0 @@ - - -# Messages - -In this section we describe the processing of the reward messages and the corresponding updates to the state. - - - - [Msg/CreateRewardProgramRequest](#msgcreaterewardprogramrequest) - - [Msg/EndRewardProgramRequest](#msgendrewardprogramrequest) - - [Msg/ClaimRewardRequest](#msgclaimrewardrequest) - - [Msg/ClaimAllRewardsRequest](#msgclaimallrewardsrequest) - - -## Msg/CreateRewardProgramRequest - -Creates a Reward Program that users can participate in. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L40-L72 - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L74-L78 - -The message will fail under the following conditions: -* The program start time is at the current block time or after -* The requester is unable to send the reward pool amount to module -* The title is empty or greater than 140 characters -* The description is empty or greater than 10000 characters -* The distribute from address is an invalid bech32 address -* The total reward pool amount is not positive -* The claim periods field is set to less than 1 -* The denominations are not in nhash -* There are no qualifying actions -* The qualifying actions are not valid - -## Msg/EndRewardProgramRequest - -Ends a Reward Program that is in either the PENDING or STARTED state. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L80-L89 - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L91-L92 - -The message will fail under the following conditions: -* The Reward Program does not end -* The Reward Program is not in PENDING or STARTED state -* The Reward Program owner does not match the specified address - -## Msg/ClaimRewardRequest - -Allows a participant to claim all their rewards for all past claim periods on a reward program. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L94-L100 - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L102-L107 - -The message will fail under the following conditions: -* The Reward Program does not exist -* The Reward Program is expired -* The Reward Address does not exist - -## Msg/ClaimAllRewardsRequest - -Allows a participant to claim all their rewards for all past claim periods on all reward programs. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L109-L113 - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/tx.proto#L115-L122 - -The message will fail under the following conditions: -* The Reward Address does not exist diff --git a/x/reward/spec/05_queries.md b/x/reward/spec/05_queries.md deleted file mode 100644 index 17ea017789..0000000000 --- a/x/reward/spec/05_queries.md +++ /dev/null @@ -1,88 +0,0 @@ - - -# Rewards Queries -In this section we describe the queries available for looking up rewards information. - - - - [Query Reward Program By ID](#query-reward-program-by-id) - - [Query Reward Programs](#query-reward-programs) - - [Query Claim Period Reward Distribution By ID](#query-claim-period-reward-distribution-by-id) - - [Query Claim Period Reward Distributions](#query-claim-period-reward-distributions) - - [Query Rewards By Address](#query-rewards-by-address) - - ---- -## Query Reward Program By ID -The `QueryRewardProgramByID` query is used to obtain the content of a specific Reward Program. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L47-L51 - -The `id` is the unique identifier for the Reward Program. - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L53-L57 - - ---- -## Query Reward Programs -The `QueryRewardPrograms` query is used to obtain the content of all Reward Programs matching the supplied `query_type`. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L59-L80 - -The `query_type` is used to filter on the Reward Program state. The following are a list of `query_types`. -* ALL - All Reward Programs will be returned. -* PENDING - All Reward Programs that are in the `PENDING` state will be returned. -* ACTIVE - All Reward Programs that are in the `STARTED` state will be returned. -* OUTSTANDING - All Reward Programs that are either in the `PENDING` or `STARTED` state will be returned. -* FINISHED - All Reward Programs that are in the `FINISHED` or `EXPIRED` state will be returned. - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L82-L88 - - ---- -## Query Claim Period Reward Distribution By ID -The `QueryClaimPeriodRewardDistributionByID` query is used to obtain the content of a specific `Claim Period Reward Distribution`. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L104-L110 - -The `reward_id` is a unique identifier for the Reward Program and the `claim_id` is a unique identifier for the Reward Program's Claim Period. - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L112-L116 - - ---- -## Query Claim Period Reward Distributions -The `QueryClaimPeriodRewardDistributions` query is used to obtain the content of all `Claim Period Reward Distributions` matching the supplied `query_type`. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L90-L94 - -The `pagination` field is used to help limit the number of results. - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L96-L102 - - ---- -## Query Rewards By Address -The `QueryRewardsByAddress` query is used to obtain the status of the address' Reward Claims. - -### Request -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L118-L126 - -The `address` field is the bech32 address of the user to list Reward Claims for. The `claim_status` is used to filter on the Reward Claim. The following are a list of `claim_status`. -* ALL - All Reward Claims are returned. -* UNCLAIMABLE - All Reward Claims that are not yet eligible to be claimed. -* CLAIMABLE - All Reward Claims that are still eligible to be claimed. -* CLAIMED - All Reward Claims that have been claimed. -* EXPIRED - All Reward Claims that have expired. - -### Response -+++ https://github.com/provenance-io/provenance/blob/243a89c76378bb5af8a8017e099ee04ac22e99ce/proto/provenance/reward/v1/query.proto#L128-L136 diff --git a/x/reward/spec/06_events.md b/x/reward/spec/06_events.md deleted file mode 100644 index 3190cb00b0..0000000000 --- a/x/reward/spec/06_events.md +++ /dev/null @@ -1,87 +0,0 @@ - - -# Events - -The rewards module emits the following events: - - - - [Reward Program Created](#reward-program-created) - - [Reward Program Started](#reward-program-started) - - [Reward Program Finished](#reward-program-finished) - - [Reward Program Expired](#reward-program-expired) - - [Reward Program Ended](#reward-program-ended) - - [Claim Rewards](#claim-rewards) - - [Claim All Rewards](#claim-all-rewards) - - ---- -## Reward Program Created - -Fires when a reward program is created with the Create Reward Program Msg. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| RewardProgramCreated | reward_program_created| \{ID string\} | - ---- -## Reward Program Started - -Fires when a reward program transitions to the STARTED state. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| RewardProgramStarted | reward_program_id | \{ID string\} | - ---- -## Reward Program Finished - -Fires when a reward program transitions to the FINISHED state. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| RewardProgramFinished | reward_program_id | \{ID string\} | - ---- -## Reward Program Expired - -Fires when a reward program transitions to the EXPIRED state. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| RewardProgramExpired | reward_program_id | \{ID string\} | - ---- -## Reward Program Ended - -Fires when a reward program is ended with the End Reward Program Msg. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| RewardProgramEnded | reward_program_id | \{ID string\} | - ---- -## Claim Rewards - -Fires when a participant claims a reward claim with Claim Reward Msg. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| ClaimRewards | reward_program_id | \{ID string\} | -| ClaimRewards | rewards_claim_address | \{bech32address string\} | - -This event will not fire if the user has no claims or if they have already claimed all their rewards. - ---- -## Claim All Rewards - -Fires when a participant claims all their reward claims with Claim Reward Msg. - -| Type | Attribute Key | Attribute Value | -| ---------------------- | --------------------- | ------------------------- | -| ClaimAllRewards | rewards_claim_address | \{bech32address string\} | - -This event will not fire if the user has no claims or if they have already claimed all their rewards. - ---- diff --git a/x/reward/spec/07_begin_and_end_blocker.md b/x/reward/spec/07_begin_and_end_blocker.md deleted file mode 100644 index 4764d3f829..0000000000 --- a/x/reward/spec/07_begin_and_end_blocker.md +++ /dev/null @@ -1,30 +0,0 @@ - - -# Begin Blocker -The `BeginBlocker` abci call is invoked on the beginning of each block. The newly created block's `BlockTime` field is used to update the `Reward Programs` and `Reward Claims`. - -## State Machine Update -The following conditional logic is evaluated to help a `Reward Program` transition between states: -1. Starts a `Reward Program` if the `BlockTime` >= `program_start_time`. -2. Evaluates if `BlockTime` >= `claim_period_end_time`, and if it evaluates to true the `Reward Program` will *attempt* to progress to the next claim period. -3. The Reward Program will successfully progress to the next claim period, if all of the following criteria are true: - 1. `remaining_pool_balance` >= `minimum_rollover_amount` - 2. `BlockTime` < `program_end_time_max` -4. If either of the previously mentioned criteria is not met, then the `Reward Program` will end. -5. A completed `Reward Program` will then expire after `reward_claim_expiration_offset` seconds from its completion time. -6. All expired `Reward Program` will return any unused funds to the reward creator and expire any unclaimed rewards. - -# End Blocker -The `EndBlocker` abci call is ran at the end of each block. The `EventManager` is monitored and `Qualifying Actions` are deduced from newly created events and prior internal state. - -## Qualifying Action Detection -The following is logic is used to detect `Qualifying Actions` and grant shares: -1. The `EventManager` is utilized to traverse the events from the newly created block. -2. Using the `QualifyingAction` types defined in each active `RewardProgram`, the module attempts to build actions using the event and any prior events. -3. Each detected action will then be evaluated and only deemed a `QualifyingAction` if it matches the `Evaluation Criteria`. -4. One or more shares will be granted to the participant performing the `QualifyingAction`. -5. Participants can then claim these shares once the claim period that they were earned in completes. -6. Detects qualifying events based on events in EventManager for reward programs currently running. -7. Gives out shares for a running rewards program for that claim period for a given address performing qualifying events. diff --git a/x/reward/spec/08_genesis.md b/x/reward/spec/08_genesis.md deleted file mode 100644 index 7b6c97e8c7..0000000000 --- a/x/reward/spec/08_genesis.md +++ /dev/null @@ -1,13 +0,0 @@ - - -# Reward Genesis - -In this section we describe the processing of the reward messages and the corresponding updates to the state. - - -## Msg/GenesisState -GenesisState contains a list of reward programs, claim period reward distributions, and reward account states. These are exported and later imported from/to the store. - -+++ https://github.com/provenance-io/provenance/blob/ccaef3a7024f0ccd73d175465e91577373127858/proto/provenance/reward/v1/genesis.proto#L13-L22 diff --git a/x/reward/spec/README.md b/x/reward/spec/README.md deleted file mode 100644 index 9a69632a36..0000000000 --- a/x/reward/spec/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# `x/rewards` - -## Overview - -The purpose of the rewards module, also known as 'Active Participation and Engagement (APE) module', is to engage and reward users for Provenance Blockchain activity. The Provenance Blockchain Foundation, or any other interested party, can create and customize their own campaigns known as Reward Programs. Provenance Blockchain users can then participate in these programs by performing the actions defined on the Reward Program. Participants will be granted shares for each qualifying action and can claim them for the respective Reward Program's reward. - -## Contents - -1. **[Concepts](01_concepts.md)** -2. **[State](02_state.md)** -3. **[State transitions](03_state_transitions.md)** -4. **[Messages](04_messages.md)** -5. **[Queries](05_queries.md)** -6. **[Events](06_events.md)** -7. **[Begin and End Blocker](07_begin_and_end_blocker.md)** -8. **[Genesis](08_genesis.md)** \ No newline at end of file diff --git a/x/reward/spec/diagrams/reward-claim/RewardClaim.png b/x/reward/spec/diagrams/reward-claim/RewardClaim.png deleted file mode 100644 index 2d946467d0..0000000000 Binary files a/x/reward/spec/diagrams/reward-claim/RewardClaim.png and /dev/null differ diff --git a/x/reward/spec/diagrams/reward-claim/reward-claim.plantuml b/x/reward/spec/diagrams/reward-claim/reward-claim.plantuml deleted file mode 100644 index 659aca0d64..0000000000 --- a/x/reward/spec/diagrams/reward-claim/reward-claim.plantuml +++ /dev/null @@ -1,22 +0,0 @@ -@startuml RewardClaim - -skinparam linetype ortho -skinparam SequenceMessageAlign center -hide empty description -state c <