diff --git a/AGENTS.md b/AGENTS.md index be05b6d2..fc4bdc6d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,7 +36,6 @@ go test ./x/photon/keeper/... -run TestMintPhoton -v **Custom modules (`x/`):** - `x/gov` — Governance wrapper augmenting Cosmos SDK gov (restricts validator voting via delegation) - `x/photon` — Photon token minting/burning with fee exceptions -- `x/dynamicfee` — Dynamic fee market calculations (ante + post handlers) - `x/coredaos` — Core DAO address management (Oversight, Photon DAOs) Each module follows standard Cosmos SDK structure: `keeper/`, `types/`, `client/cli/`, `ante/` or `post/`, `testutil/`, `module.go`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d269618..926156e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ ### IMPROVEMENTS +- migrate `x/coredaos` away from atomone `x/gov` wrapper [#353](https://github.com/atomone-hub/atomone/pull/353) + ## v4.0.0 *Jun 23nd, 2026* diff --git a/Makefile b/Makefile index 18e1735d..a9c953db 100644 --- a/Makefile +++ b/Makefile @@ -271,8 +271,6 @@ mockgen_cmd=$(rundep) github.com/golang/mock/mockgen mocks-gen: $(mockgen_cmd) -source=x/photon/types/expected_keepers.go -package testutil -destination x/photon/testutil/expected_keepers_mocks.go $(mockgen_cmd) -source=x/photon/ante/expected_keepers.go -package ante_test -destination x/photon/ante/expected_keepers_mocks_test.go - $(mockgen_cmd) -source=x/dynamicfee/ante/expected_keepers.go -package ante_test -destination x/dynamicfee/ante/expected_keepers_mocks_test.go - $(mockgen_cmd) -source=x/dynamicfee/post/expected_keepers.go -package post_test -destination x/dynamicfee/post/expected_keepers_mocks_test.go $(mockgen_cmd) -source=x/coredaos/types/expected_keepers.go -package testutil -destination x/coredaos/testutil/expected_keepers_mocks.go .PHONY: docker-build-debug docker-build-hermes docker-build-all mocks-gen diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 8cb7f85e..7b41038b 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -283,7 +283,7 @@ func NewAppKeeper( appCodec, runtime.NewKVStoreService(appKeepers.keys[coredaostypes.StoreKey]), authtypes.NewModuleAddress(govtypes.ModuleName).String(), - appKeepers.GovKeeperWrapper, + appKeepers.GovKeeper, appKeepers.StakingKeeper, ) diff --git a/app/modules.go b/app/modules.go index 9ba689a8..f3f13001 100644 --- a/app/modules.go +++ b/app/modules.go @@ -117,7 +117,7 @@ func appModules( sdkparams.NewAppModule(app.ParamsKeeper), consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), dynamicfee.NewAppModule(appCodec, *app.DynamicfeeKeeper), - coredaos.NewAppModule(appCodec, *app.CoreDaosKeeper, app.GovKeeperWrapper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + coredaos.NewAppModule(appCodec, *app.CoreDaosKeeper, app.GovKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), epochs.NewAppModule(&app.EpochsKeeper), app.TransferModule, diff --git a/x/coredaos/keeper/keeper.go b/x/coredaos/keeper/keeper.go index 39179e8d..eb74e4e0 100644 --- a/x/coredaos/keeper/keeper.go +++ b/x/coredaos/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/atomone-hub/atomone/x/coredaos/types" ) @@ -16,7 +17,7 @@ type Keeper struct { storeService store.KVStoreService authority string - govKeeper types.GovKeeper + govKeeper *govkeeper.Keeper stakingKeeper types.StakingKeeper Schema collections.Schema @@ -27,7 +28,7 @@ func NewKeeper( cdc codec.BinaryCodec, storeService store.KVStoreService, authority string, - govKeeper types.GovKeeper, + govKeeper *govkeeper.Keeper, stakingKeeper types.StakingKeeper, ) *Keeper { if _, err := sdk.AccAddressFromBech32(authority); err != nil { diff --git a/x/coredaos/keeper/msg_server.go b/x/coredaos/keeper/msg_server.go index d338fa81..d2e6effa 100644 --- a/x/coredaos/keeper/msg_server.go +++ b/x/coredaos/keeper/msg_server.go @@ -4,15 +4,15 @@ import ( "context" "fmt" + "cosmossdk.io/collections" "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkgovtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/atomone-hub/atomone/x/coredaos/types" - govtypes "github.com/atomone-hub/atomone/x/gov/types" - govtypesv1 "github.com/atomone-hub/atomone/x/gov/types/v1" ) var _ types.MsgServer = (*MsgServer)(nil) @@ -107,17 +107,17 @@ func (ms MsgServer) AnnotateProposal(goCtx context.Context, msg *types.MsgAnnota return nil, types.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", params.SteeringDaoAddress, msg.Annotator) } - proposal, found := ms.k.govKeeper.GetProposal(ctx, msg.ProposalId) - if !found { + proposal, err := ms.k.govKeeper.Proposals.Get(ctx, msg.ProposalId) + if err != nil { logger.Error( "proposal not found", "proposal_id", msg.ProposalId, "authority", msg.Annotator, ) - return nil, govtypes.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) + return nil, types.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) } - if proposal.Status != govtypesv1.StatusVotingPeriod { + if proposal.Status != govv1.StatusVotingPeriod { logger.Error( "proposal is not in voting period", "proposal", proposal.Id, @@ -140,7 +140,9 @@ func (ms MsgServer) AnnotateProposal(goCtx context.Context, msg *types.MsgAnnota } proposal.Annotation = msg.Annotation - ms.k.govKeeper.SetProposal(ctx, proposal) + if err := ms.k.govKeeper.SetProposal(ctx, proposal); err != nil { + return nil, errors.Wrapf(err, "error setting proposal") + } logger.Info( "proposal annotated", @@ -185,17 +187,17 @@ func (ms MsgServer) EndorseProposal(goCtx context.Context, msg *types.MsgEndorse return nil, types.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", params.SteeringDaoAddress, msg.Endorser) } - proposal, found := ms.k.govKeeper.GetProposal(ctx, msg.ProposalId) - if !found { + proposal, err := ms.k.govKeeper.Proposals.Get(ctx, msg.ProposalId) + if err != nil { logger.Error( "proposal not found", "proposal_id", msg.ProposalId, "authority", msg.Endorser, ) - return nil, govtypes.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) + return nil, types.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) } - if proposal.Status != govtypesv1.StatusVotingPeriod { + if proposal.Status != govv1.StatusVotingPeriod { logger.Error( "proposal is not in voting period", "proposal", proposal.Id, @@ -216,7 +218,9 @@ func (ms MsgServer) EndorseProposal(goCtx context.Context, msg *types.MsgEndorse } proposal.Endorsed = true - ms.k.govKeeper.SetProposal(ctx, proposal) + if err := ms.k.govKeeper.SetProposal(ctx, proposal); err != nil { + return nil, errors.Wrapf(err, "error setting proposal") + } logger.Info( "proposal endorsed", @@ -274,17 +278,17 @@ func (ms MsgServer) ExtendVotingPeriod(goCtx context.Context, msg *types.MsgExte return nil, types.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", addressesString, msg.Extender) } - proposal, found := ms.k.govKeeper.GetProposal(ctx, msg.ProposalId) - if !found { + proposal, err := ms.k.govKeeper.Proposals.Get(ctx, msg.ProposalId) + if err != nil { logger.Error( "proposal not found", "proposal_id", msg.ProposalId, "authority", msg.Extender, ) - return nil, govtypes.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) + return nil, types.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) } - if proposal.Status != govtypesv1.StatusVotingPeriod { + if proposal.Status != govv1.StatusVotingPeriod { logger.Error( "proposal is not in voting period", "proposal", proposal.Id, @@ -308,12 +312,18 @@ func (ms MsgServer) ExtendVotingPeriod(goCtx context.Context, msg *types.MsgExte newEndTime := proposal.VotingEndTime.Add(*params.VotingPeriodExtensionDuration) // Update ActiveProposalsQueue with new VotingEndTime - ms.k.govKeeper.RemoveFromActiveProposalQueue(ctx, proposal.Id, *proposal.VotingEndTime) + if err := ms.k.govKeeper.ActiveProposalsQueue.Remove(ctx, collections.Join(*proposal.VotingEndTime, proposal.Id)); err != nil { + return nil, errors.Wrapf(err, "error removing proposal from active proposal queue") + } proposal.VotingEndTime = &newEndTime - ms.k.govKeeper.InsertActiveProposalQueue(ctx, proposal.Id, *proposal.VotingEndTime) + if err := ms.k.govKeeper.ActiveProposalsQueue.Set(ctx, collections.Join(*proposal.VotingEndTime, proposal.Id), proposal.Id); err != nil { + return nil, errors.Wrapf(err, "error inserting proposal into active proposal queue") + } proposal.TimesVotingPeriodExtended++ - ms.k.govKeeper.SetProposal(ctx, proposal) + if err := ms.k.govKeeper.SetProposal(ctx, proposal); err != nil { + return nil, errors.Wrapf(err, "error setting proposal") + } logger.Info( "voting period extended", @@ -362,17 +372,17 @@ func (ms MsgServer) VetoProposal(goCtx context.Context, msg *types.MsgVetoPropos return nil, types.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", params.OversightDaoAddress, msg.Vetoer) } - proposal, found := ms.k.govKeeper.GetProposal(ctx, msg.ProposalId) - if !found { + proposal, err := ms.k.govKeeper.Proposals.Get(ctx, msg.ProposalId) + if err != nil { logger.Error( "proposal not found", "proposal_id", msg.ProposalId, "authority", msg.Vetoer, ) - return nil, govtypes.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) + return nil, types.ErrUnknownProposal.Wrapf("proposal with ID %d not found", msg.ProposalId) } - if proposal.Status != govtypesv1.StatusVotingPeriod { + if proposal.Status != govv1.StatusVotingPeriod { logger.Error( "proposal is not in voting period", "proposal", proposal.Id, @@ -411,23 +421,35 @@ func (ms MsgServer) VetoProposal(goCtx context.Context, msg *types.MsgVetoPropos // follows the same logic as in x/gov/abci.go for rejected proposals if msg.BurnDeposit { - ms.k.govKeeper.DeleteAndBurnDeposits(ctx, proposal.Id) + if err := ms.k.govKeeper.DeleteAndBurnDeposits(ctx, proposal.Id); err != nil { + return nil, errors.Wrapf(err, "error deleting and burning deposits") + } } else { - ms.k.govKeeper.RefundAndDeleteDeposits(ctx, proposal.Id) + if err := ms.k.govKeeper.RefundAndDeleteDeposits(ctx, proposal.Id); err != nil { + return nil, errors.Wrapf(err, "error refunding and deleting deposits") + } } - proposal.Status = govtypesv1.StatusVetoed + proposal.Status = govv1.StatusVetoed // Since the proposal is vetoed, we set the final tally result to an empty tally // and the voting period ends immediately - emptyTally := govtypesv1.EmptyTallyResult() + emptyTally := govv1.EmptyTallyResult() proposal.FinalTallyResult = &emptyTally origEndTime := proposal.VotingEndTime blockTime := ctx.BlockTime() proposal.VotingEndTime = &blockTime - ms.k.govKeeper.SetProposal(ctx, proposal) - ms.k.govKeeper.DeleteVotes(ctx, proposal.Id) - ms.k.govKeeper.RemoveFromActiveProposalQueue(ctx, proposal.Id, *origEndTime) + if err := ms.k.govKeeper.SetProposal(ctx, proposal); err != nil { + return nil, errors.Wrapf(err, "error setting proposal") + } + // Delete all votes for the proposal. Votes are stored as + // collections.Map[collections.Pair[uint64, sdk.AccAddress], v1.Vote]. + if err := ms.k.govKeeper.Votes.Clear(ctx, collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)); err != nil { + return nil, errors.Wrapf(err, "error deleting votes") + } + if err := ms.k.govKeeper.ActiveProposalsQueue.Remove(ctx, collections.Join(*origEndTime, proposal.Id)); err != nil { + return nil, errors.Wrapf(err, "error removing proposal from active proposal queue") + } ms.k.govKeeper.UpdateMinInitialDeposit(ctx, true) ms.k.govKeeper.UpdateMinDeposit(ctx, true) diff --git a/x/coredaos/keeper/msg_server_test.go b/x/coredaos/keeper/msg_server_test.go index 7f66d27a..ef16a233 100644 --- a/x/coredaos/keeper/msg_server_test.go +++ b/x/coredaos/keeper/msg_server_test.go @@ -7,17 +7,25 @@ import ( "github.com/stretchr/testify/require" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "cosmossdk.io/collections" "cosmossdk.io/math" codectypes "github.com/cosmos/cosmos-sdk/codec/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - sdktx "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + atomoneapp "github.com/atomone-hub/atomone/app" + "github.com/atomone-hub/atomone/app/helpers" + coredaoskeeper "github.com/atomone-hub/atomone/x/coredaos/keeper" "github.com/atomone-hub/atomone/x/coredaos/testutil" "github.com/atomone-hub/atomone/x/coredaos/types" - govtypesv1 "github.com/atomone-hub/atomone/x/gov/types/v1" ) func TestMsgServerUpdateParams(t *testing.T) { @@ -282,41 +290,67 @@ func TestMsgServerUpdateParams(t *testing.T) { } } +// govModuleAddr returns the gov module account address, which is the required +// signer for all messages contained in a governance proposal. +func govModuleAddr() string { + return authtypes.NewModuleAddress(govtypes.ModuleName).String() +} + +// collectionsJoin is a small alias to build the composite key used by the +// gov ActiveProposalsQueue (Pair[time.Time, uint64]). +func collectionsJoin(t time.Time, id uint64) collections.Pair[time.Time, uint64] { + return collections.Join(t, id) +} + +// submitProposalReal submits a proposal with the given messages into the real +// gov keeper. The proposal is left in the deposit period unless activate is +// true, in which case it is moved to the voting period. +func submitProposalReal(t *testing.T, app *atomoneapp.AtomOneApp, ctx sdk.Context, msgs []sdk.Msg, activate bool) govv1.Proposal { + t.Helper() + govAddr := authtypes.NewModuleAddress(govtypes.ModuleName) + proposal, err := app.GovKeeper.SubmitProposal(ctx, msgs, "", "title", "summary", govAddr) + require.NoError(t, err) + if activate { + require.NoError(t, app.GovKeeper.ActivateVotingPeriod(ctx, proposal)) + // re-fetch to get the populated VotingEndTime / StatusVotingPeriod + proposal, err = app.GovKeeper.Proposals.Get(ctx, proposal.Id) + require.NoError(t, err) + } + return proposal +} + +// submitBankSendProposalReal submits a proposal whose only message is a bank +// MsgSend signed by the gov module account. +func submitBankSendProposalReal(t *testing.T, app *atomoneapp.AtomOneApp, ctx sdk.Context, activate bool) govv1.Proposal { + t.Helper() + govAddr := authtypes.NewModuleAddress(govtypes.ModuleName) + recipient := sdk.AccAddress([]byte("recipient___________")) + sendMsg := banktypes.NewMsgSend(govAddr, recipient, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(1)))) + return submitProposalReal(t, app, ctx, []sdk.Msg{sendMsg}, activate) +} + func TestMsgServerAnnotateProposal(t *testing.T) { testAcc := simtestutil.CreateRandomAccounts(2) annotatorAcc := testAcc[0].String() steeringDAOAcc := testAcc[1].String() - votingPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - } - votingPeriodProposalWithAnnotation := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - Annotation: "Something", - } - depositPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 2, - Status: govtypesv1.StatusDepositPeriod, - } + tests := []struct { - name string - msg *types.MsgAnnotateProposal - expectedErr string - setupMocks func(sdk.Context, *testutil.Mocks) + name string + msg *types.MsgAnnotateProposal + expectedErr string + // proposalState describes how to set up the proposal in the real gov + // keeper before calling the method: "none", "voting", "deposit", + // "voting-annotated". + proposalState string setSteeringDAO bool + // assertAnnotation, if non-empty, is the expected annotation after a + // successful call. + assertAnnotation string }{ { name: "empty msg", msg: &types.MsgAnnotateProposal{}, expectedErr: "invalid annotator address: empty address string is not allowed: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong addr annotator", @@ -324,7 +358,6 @@ func TestMsgServerAnnotateProposal(t *testing.T) { Annotator: "cosmosincorrectaddress", }, expectedErr: "invalid annotator address: decoding bech32 failed: invalid separator index -1: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "empty annotation", @@ -332,7 +365,6 @@ func TestMsgServerAnnotateProposal(t *testing.T) { Annotator: annotatorAcc, }, expectedErr: "annotation cannot be empty: invalid request", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "annotator empty annotation", @@ -341,7 +373,6 @@ func TestMsgServerAnnotateProposal(t *testing.T) { Annotation: "Something", }, expectedErr: "Steering DAO address is not set: function is disabled", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong annotator account", @@ -350,7 +381,6 @@ func TestMsgServerAnnotateProposal(t *testing.T) { Annotation: "Something", }, expectedErr: "invalid authority; expected " + steeringDAOAcc + ", got " + annotatorAcc + ": expected core DAO account as only signer for this message", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, setSteeringDAO: true, }, { @@ -358,11 +388,10 @@ func TestMsgServerAnnotateProposal(t *testing.T) { msg: &types.MsgAnnotateProposal{ Annotator: steeringDAOAcc, Annotation: "Something", + ProposalId: 9999, }, - expectedErr: "proposal with ID 0 not found: unknown proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(govtypesv1.Proposal{}, false) - }, + expectedErr: "proposal with ID 9999 not found: unknown proposal", + proposalState: "none", setSteeringDAO: true, }, { @@ -370,25 +399,19 @@ func TestMsgServerAnnotateProposal(t *testing.T) { msg: &types.MsgAnnotateProposal{ Annotator: steeringDAOAcc, Annotation: "Something", - ProposalId: 1, - }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - call1 := m.GovKeeper.EXPECT().GetProposal(ctx, uint64(1)).Return(votingPeriodProposal, true) - m.GovKeeper.EXPECT().SetProposal(ctx, votingPeriodProposalWithAnnotation).After(call1) }, - setSteeringDAO: true, + proposalState: "voting", + setSteeringDAO: true, + assertAnnotation: "Something", }, { name: "proposal not in voting period", msg: &types.MsgAnnotateProposal{ Annotator: steeringDAOAcc, Annotation: "Something", - ProposalId: 2, - }, - expectedErr: "proposal with ID 2 is not in voting period: inactive proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(2)).Return(depositPeriodProposal, true) }, + expectedErr: "is not in voting period: inactive proposal", + proposalState: "deposit", setSteeringDAO: true, }, { @@ -396,38 +419,50 @@ func TestMsgServerAnnotateProposal(t *testing.T) { msg: &types.MsgAnnotateProposal{ Annotator: steeringDAOAcc, Annotation: "Something", - ProposalId: 3, - }, - expectedErr: "proposal with ID 3 already has an annotation: annotation already present", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(3)).Return(votingPeriodProposalWithAnnotation, true) }, + expectedErr: "already has an annotation: annotation already present", + proposalState: "voting-annotated", setSteeringDAO: true, }, { name: "already annotated proposal but overwrite", msg: &types.MsgAnnotateProposal{ Annotator: steeringDAOAcc, - Annotation: "Something", - ProposalId: 3, + Annotation: "New annotation", Overwrite: true, }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(3)).Return(votingPeriodProposalWithAnnotation, true) - m.GovKeeper.EXPECT().SetProposal(ctx, votingPeriodProposalWithAnnotation) - }, - setSteeringDAO: true, + proposalState: "voting-annotated", + setSteeringDAO: true, + assertAnnotation: "New annotation", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ms, k, m, ctx := testutil.SetupMsgServer(t) - tt.setupMocks(ctx, &m) + app := helpers.Setup(t) + ctx := app.NewUncachedContext(true, tmproto.Header{Time: time.Now()}) + ms := coredaoskeeper.NewMsgServer(app.CoreDaosKeeper) + params := types.DefaultParams() if tt.setSteeringDAO { params.SteeringDaoAddress = steeringDAOAcc } - require.NoError(t, k.Params.Set(ctx, params)) + require.NoError(t, app.CoreDaosKeeper.Params.Set(ctx, params)) + + // Set up the proposal in the real gov keeper as required. + switch tt.proposalState { + case "voting": + p := submitBankSendProposalReal(t, app, ctx, true) + tt.msg.ProposalId = p.Id + case "deposit": + p := submitBankSendProposalReal(t, app, ctx, false) + tt.msg.ProposalId = p.Id + case "voting-annotated": + p := submitBankSendProposalReal(t, app, ctx, true) + p.Annotation = "Existing" + require.NoError(t, app.GovKeeper.SetProposal(ctx, p)) + tt.msg.ProposalId = p.Id + } + if err := tt.msg.ValidateBasic(); err != nil { if tt.expectedErr != "" { require.EqualError(t, err, tt.expectedErr) @@ -437,10 +472,16 @@ func TestMsgServerAnnotateProposal(t *testing.T) { } _, err := ms.AnnotateProposal(ctx, tt.msg) if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr) return } require.NoError(t, err) + if tt.assertAnnotation != "" { + got, err := app.GovKeeper.Proposals.Get(ctx, tt.msg.ProposalId) + require.NoError(t, err) + require.Equal(t, tt.assertAnnotation, got.Annotation) + } }) } } @@ -449,37 +490,19 @@ func TestMsgServerEndorseProposal(t *testing.T) { testAcc := simtestutil.CreateRandomAccounts(2) endorserAcc := testAcc[0].String() steeringDAOAcc := testAcc[1].String() - votingPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - } - votingPeriodProposalWithEndorsement := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - Endorsed: true, - } - depositPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 2, - Status: govtypesv1.StatusDepositPeriod, - } + tests := []struct { name string msg *types.MsgEndorseProposal expectedErr string - setupMocks func(sdk.Context, *testutil.Mocks) + proposalState string setSteeringDAO bool + assertEndorsed bool }{ { name: "empty msg", msg: &types.MsgEndorseProposal{}, expectedErr: "invalid endorser address: empty address string is not allowed: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong addr endorser", @@ -487,7 +510,6 @@ func TestMsgServerEndorseProposal(t *testing.T) { Endorser: "cosmosincorrectaddress", }, expectedErr: "invalid endorser address: decoding bech32 failed: invalid separator index -1: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "no steeringdao address", @@ -495,7 +517,6 @@ func TestMsgServerEndorseProposal(t *testing.T) { Endorser: endorserAcc, }, expectedErr: "Steering DAO address is not set: function is disabled", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong endorser account", @@ -503,66 +524,72 @@ func TestMsgServerEndorseProposal(t *testing.T) { Endorser: endorserAcc, }, expectedErr: "invalid authority; expected " + steeringDAOAcc + ", got " + endorserAcc + ": expected core DAO account as only signer for this message", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, setSteeringDAO: true, }, { name: "non existing proposal", msg: &types.MsgEndorseProposal{ - Endorser: steeringDAOAcc, - }, - expectedErr: "proposal with ID 0 not found: unknown proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(govtypesv1.Proposal{}, false) + Endorser: steeringDAOAcc, + ProposalId: 9999, }, + expectedErr: "proposal with ID 9999 not found: unknown proposal", + proposalState: "none", setSteeringDAO: true, }, { name: "ok", msg: &types.MsgEndorseProposal{ - Endorser: steeringDAOAcc, - ProposalId: 1, - }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(1)).Return(votingPeriodProposal, true) - m.GovKeeper.EXPECT().SetProposal(ctx, votingPeriodProposalWithEndorsement) + Endorser: steeringDAOAcc, }, + proposalState: "voting", setSteeringDAO: true, + assertEndorsed: true, }, { name: "proposal not in voting period", msg: &types.MsgEndorseProposal{ - Endorser: steeringDAOAcc, - ProposalId: 2, - }, - expectedErr: "proposal with ID 2 is not in voting period: inactive proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(2)).Return(depositPeriodProposal, true) + Endorser: steeringDAOAcc, }, + expectedErr: "is not in voting period: inactive proposal", + proposalState: "deposit", setSteeringDAO: true, }, { name: "already endorsed proposal", msg: &types.MsgEndorseProposal{ - Endorser: steeringDAOAcc, - ProposalId: 3, - }, - expectedErr: "proposal with ID 3 has already been endorsed: proposal already endorsed", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(3)).Return(votingPeriodProposalWithEndorsement, true) + Endorser: steeringDAOAcc, }, + expectedErr: "has already been endorsed: proposal already endorsed", + proposalState: "voting-endorsed", setSteeringDAO: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ms, k, m, ctx := testutil.SetupMsgServer(t) - tt.setupMocks(ctx, &m) + app := helpers.Setup(t) + ctx := app.NewUncachedContext(true, tmproto.Header{Time: time.Now()}) + ms := coredaoskeeper.NewMsgServer(app.CoreDaosKeeper) + params := types.DefaultParams() if tt.setSteeringDAO { params.SteeringDaoAddress = steeringDAOAcc } - require.NoError(t, k.Params.Set(ctx, params)) + require.NoError(t, app.CoreDaosKeeper.Params.Set(ctx, params)) + + switch tt.proposalState { + case "voting": + p := submitBankSendProposalReal(t, app, ctx, true) + tt.msg.ProposalId = p.Id + case "deposit": + p := submitBankSendProposalReal(t, app, ctx, false) + tt.msg.ProposalId = p.Id + case "voting-endorsed": + p := submitBankSendProposalReal(t, app, ctx, true) + p.Endorsed = true + require.NoError(t, app.GovKeeper.SetProposal(ctx, p)) + tt.msg.ProposalId = p.Id + } + if err := tt.msg.ValidateBasic(); err != nil { if tt.expectedErr != "" { require.EqualError(t, err, tt.expectedErr) @@ -572,10 +599,16 @@ func TestMsgServerEndorseProposal(t *testing.T) { } _, err := ms.EndorseProposal(ctx, tt.msg) if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr) return } require.NoError(t, err) + if tt.assertEndorsed { + got, err := app.GovKeeper.Proposals.Get(ctx, tt.msg.ProposalId) + require.NoError(t, err) + require.True(t, got.Endorsed) + } }) } } @@ -584,52 +617,25 @@ func TestMsgServerExtendVotingPeriod(t *testing.T) { testAcc := simtestutil.CreateRandomAccounts(2) extenderAcc := testAcc[0].String() steeringDAOAcc := testAcc[1].String() - params := types.DefaultParams() - votingEndTime := time.Now().Add(time.Hour * time.Duration(1)) - votingEndTimeExtended := votingEndTime.Add(*params.VotingPeriodExtensionDuration) - - votingPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - VotingEndTime: &votingEndTime, - TimesVotingPeriodExtended: 2, - } - votingPeriodProposalWithExtension := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - VotingEndTime: &votingEndTimeExtended, - TimesVotingPeriodExtended: 3, - } - depositPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 2, - Status: govtypesv1.StatusDepositPeriod, - VotingEndTime: &votingEndTime, - } + tests := []struct { name string msg *types.MsgExtendVotingPeriod expectedErr string - setupMocks func(sdk.Context, *testutil.Mocks) + proposalState string setSteeringDAO bool + assertExtended bool }{ { name: "empty msg", msg: &types.MsgExtendVotingPeriod{}, expectedErr: "invalid extender address: empty address string is not allowed: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong addr extender", msg: &types.MsgExtendVotingPeriod{ Extender: "cosmosincorrectaddress", }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, expectedErr: "invalid extender address: decoding bech32 failed: invalid separator index -1: invalid address", }, { @@ -637,7 +643,6 @@ func TestMsgServerExtendVotingPeriod(t *testing.T) { msg: &types.MsgExtendVotingPeriod{ Extender: extenderAcc, }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, expectedErr: "Steering DAO address and Oversight DAO address are not set: function is disabled", }, { @@ -646,68 +651,74 @@ func TestMsgServerExtendVotingPeriod(t *testing.T) { Extender: extenderAcc, }, expectedErr: "invalid authority; expected " + steeringDAOAcc + ", got " + extenderAcc + ": expected core DAO account as only signer for this message", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, setSteeringDAO: true, }, { name: "non existing proposal", msg: &types.MsgExtendVotingPeriod{ - Extender: steeringDAOAcc, - }, - expectedErr: "proposal with ID 0 not found: unknown proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(govtypesv1.Proposal{}, false) + Extender: steeringDAOAcc, + ProposalId: 9999, }, + expectedErr: "proposal with ID 9999 not found: unknown proposal", + proposalState: "none", setSteeringDAO: true, }, { name: "ok", msg: &types.MsgExtendVotingPeriod{ - Extender: steeringDAOAcc, - ProposalId: 1, - }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(1)).Return(votingPeriodProposal, true) - m.GovKeeper.EXPECT().RemoveFromActiveProposalQueue(ctx, uint64(1), votingEndTime) - m.GovKeeper.EXPECT().InsertActiveProposalQueue(ctx, uint64(1), votingEndTimeExtended) - m.GovKeeper.EXPECT().SetProposal(ctx, votingPeriodProposalWithExtension) + Extender: steeringDAOAcc, }, + proposalState: "voting", setSteeringDAO: true, + assertExtended: true, }, { name: "proposal not in voting period", msg: &types.MsgExtendVotingPeriod{ - Extender: steeringDAOAcc, - ProposalId: 2, - }, - expectedErr: "proposal with ID 2 is not in voting period: inactive proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(2)).Return(depositPeriodProposal, true) + Extender: steeringDAOAcc, }, + expectedErr: "is not in voting period: inactive proposal", + proposalState: "deposit", setSteeringDAO: true, }, { name: "proposal cannot be extended", msg: &types.MsgExtendVotingPeriod{ - Extender: steeringDAOAcc, - ProposalId: 3, - }, - expectedErr: "proposal with ID 3 has reached the maximum number of voting period extensions: invalid proposal content", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(3)).Return(votingPeriodProposalWithExtension, true) + Extender: steeringDAOAcc, }, + expectedErr: "has reached the maximum number of voting period extensions: invalid proposal content", + proposalState: "voting-maxed", setSteeringDAO: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ms, k, m, ctx := testutil.SetupMsgServer(t) - tt.setupMocks(ctx, &m) + app := helpers.Setup(t) + ctx := app.NewUncachedContext(true, tmproto.Header{Time: time.Now()}) + ms := coredaoskeeper.NewMsgServer(app.CoreDaosKeeper) + params := types.DefaultParams() if tt.setSteeringDAO { params.SteeringDaoAddress = steeringDAOAcc } - require.NoError(t, k.Params.Set(ctx, params)) + require.NoError(t, app.CoreDaosKeeper.Params.Set(ctx, params)) + + var origEndTime time.Time + switch tt.proposalState { + case "voting": + p := submitBankSendProposalReal(t, app, ctx, true) + origEndTime = *p.VotingEndTime + tt.msg.ProposalId = p.Id + case "deposit": + p := submitBankSendProposalReal(t, app, ctx, false) + tt.msg.ProposalId = p.Id + case "voting-maxed": + p := submitBankSendProposalReal(t, app, ctx, true) + p.TimesVotingPeriodExtended = params.VotingPeriodExtensionsLimit + require.NoError(t, app.GovKeeper.SetProposal(ctx, p)) + tt.msg.ProposalId = p.Id + } + if err := tt.msg.ValidateBasic(); err != nil { if tt.expectedErr != "" { require.EqualError(t, err, tt.expectedErr) @@ -717,10 +728,22 @@ func TestMsgServerExtendVotingPeriod(t *testing.T) { } _, err := ms.ExtendVotingPeriod(ctx, tt.msg) if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr) return } require.NoError(t, err) + if tt.assertExtended { + got, err := app.GovKeeper.Proposals.Get(ctx, tt.msg.ProposalId) + require.NoError(t, err) + expectedEndTime := origEndTime.Add(*params.VotingPeriodExtensionDuration) + require.WithinDuration(t, expectedEndTime, *got.VotingEndTime, time.Second) + require.Equal(t, uint32(1), got.TimesVotingPeriodExtended) + // the proposal must have been re-queued under the new end time + has, err := app.GovKeeper.ActiveProposalsQueue.Has(ctx, collectionsJoin(*got.VotingEndTime, got.Id)) + require.NoError(t, err) + require.True(t, has) + } }) } } @@ -729,110 +752,21 @@ func TestMsgServerVetoProposal(t *testing.T) { testAcc := simtestutil.CreateRandomAccounts(3) vetoerAcc := testAcc[0].String() oversightDAOAcc := testAcc[1].String() - emptyTally := govtypesv1.EmptyTallyResult() - votingEndTime := time.Now().Add(time.Hour * time.Duration(1)) - votingPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVotingPeriod, - VotingEndTime: &votingEndTime, - } - proposalWithVeto := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 1, - Status: govtypesv1.StatusVetoed, - FinalTallyResult: &emptyTally, - VotingEndTime: &votingEndTime, // will be overwritten in the test - } - depositPeriodProposal := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal", - Id: 2, - Status: govtypesv1.StatusDepositPeriod, - } - proposalWithChangeOversightDAOMsgs, err := sdktx.SetMsgs([]sdk.Msg{&types.MsgUpdateParams{ - Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", - Params: types.Params{ - OversightDaoAddress: testAcc[2].String(), - }, - }}) - require.NoError(t, err) - proposalWithChangeOversightDAO := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal to change oversight DAO address", - Id: 4, - Status: govtypesv1.StatusVotingPeriod, - Messages: proposalWithChangeOversightDAOMsgs, - } - proposalWithEmptyOversightDAOMsgs, err := sdktx.SetMsgs([]sdk.Msg{&types.MsgUpdateParams{ - Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", - Params: types.Params{ - OversightDaoAddress: "", - }, - }}) - require.NoError(t, err) - proposalWithEmptyOversightDAO := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal to set empty oversight DAO address", - Id: 5, - Status: govtypesv1.StatusVotingPeriod, - Messages: proposalWithEmptyOversightDAOMsgs, - } - - // authz-wrapped proposals: MsgUpdateParams inside authz.MsgExec - govAuthority := "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" - innerChangeAny, err := codectypes.NewAnyWithValue(&types.MsgUpdateParams{ - Authority: govAuthority, - Params: types.Params{OversightDaoAddress: testAcc[2].String()}, - }) - require.NoError(t, err) - wrappedChangeMsgs, err := sdktx.SetMsgs([]sdk.Msg{&authz.MsgExec{ - Grantee: govAuthority, - Msgs: []*codectypes.Any{innerChangeAny}, - }}) - require.NoError(t, err) - proposalWithWrappedChangeOversightDAO := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal to change oversight DAO address wrapped in authz.MsgExec", - Id: 6, - Status: govtypesv1.StatusVotingPeriod, - Messages: wrappedChangeMsgs, - } - - // double-wrapped: MsgUpdateParams inside MsgExec inside MsgExec - execAny, err := codectypes.NewAnyWithValue(&authz.MsgExec{ - Grantee: govAuthority, - Msgs: []*codectypes.Any{innerChangeAny}, - }) - require.NoError(t, err) - doubleWrappedChangeMsgs, err := sdktx.SetMsgs([]sdk.Msg{&authz.MsgExec{ - Grantee: govAuthority, - Msgs: []*codectypes.Any{execAny}, - }}) - require.NoError(t, err) - proposalWithDoubleWrappedChangeOversightDAO := govtypesv1.Proposal{ - Title: "Test Proposal", - Summary: "A proposal to change oversight DAO address double-wrapped in authz.MsgExec", - Id: 7, - Status: govtypesv1.StatusVotingPeriod, - Messages: doubleWrappedChangeMsgs, - } + newOversightAddr := testAcc[2].String() + extDuration := time.Hour tests := []struct { name string msg *types.MsgVetoProposal expectedErr string - setupMocks func(sdk.Context, *testutil.Mocks) - setSteeringDAO bool + proposalState string setOversightDAO bool + assertVetoed bool }{ { name: "empty msg", msg: &types.MsgVetoProposal{}, expectedErr: "invalid vetoer address: empty address string is not allowed: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong addr vetoer", @@ -840,7 +774,6 @@ func TestMsgServerVetoProposal(t *testing.T) { Vetoer: "cosmosincorrectaddress", }, expectedErr: "invalid vetoer address: decoding bech32 failed: invalid separator index -1: invalid address", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "function disabled", @@ -848,7 +781,6 @@ func TestMsgServerVetoProposal(t *testing.T) { Vetoer: vetoerAcc, }, expectedErr: "Oversight DAO address is not set: function is disabled", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, }, { name: "wrong vetoer account", @@ -856,147 +788,142 @@ func TestMsgServerVetoProposal(t *testing.T) { Vetoer: vetoerAcc, }, expectedErr: "invalid authority; expected " + oversightDAOAcc + ", got " + vetoerAcc + ": expected core DAO account as only signer for this message", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) {}, setOversightDAO: true, }, { name: "non existing proposal", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - }, - expectedErr: "proposal with ID 0 not found: unknown proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(govtypesv1.Proposal{}, false) + Vetoer: oversightDAOAcc, + ProposalId: 9999, }, + expectedErr: "proposal with ID 9999 not found: unknown proposal", + proposalState: "none", setOversightDAO: true, }, { name: "ok", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 1, - }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - // ensure proposalWithVeto has the correct VotingEndTime set - // can only do it here because ctx is needed - newVotingEndTime := ctx.BlockTime() - proposalWithVeto.VotingEndTime = &newVotingEndTime - - call1 := m.GovKeeper.EXPECT().GetProposal(ctx, uint64(1)).Return(votingPeriodProposal, true) - m.GovKeeper.EXPECT().RefundAndDeleteDeposits(ctx, uint64(1)).After(call1) - m.GovKeeper.EXPECT().SetProposal(ctx, proposalWithVeto).After(call1) - m.GovKeeper.EXPECT().DeleteVotes(ctx, uint64(1)).After(call1) - call2 := m.GovKeeper.EXPECT().RemoveFromActiveProposalQueue(ctx, uint64(1), votingEndTime).After(call1) - m.GovKeeper.EXPECT().UpdateMinInitialDeposit(ctx, true).After(call2) - m.GovKeeper.EXPECT().UpdateMinDeposit(ctx, true).After(call2) + Vetoer: oversightDAOAcc, }, + proposalState: "voting", setOversightDAO: true, + assertVetoed: true, }, { name: "ok burn deposit", msg: &types.MsgVetoProposal{ Vetoer: oversightDAOAcc, - ProposalId: 1, BurnDeposit: true, }, - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - // ensure proposalWithVeto has the correct VotingEndTime set - // can only do it here because ctx is needed - newVotingEndTime := ctx.BlockTime() - proposalWithVeto.VotingEndTime = &newVotingEndTime - - call1 := m.GovKeeper.EXPECT().GetProposal(ctx, uint64(1)).Return(votingPeriodProposal, true) - m.GovKeeper.EXPECT().DeleteAndBurnDeposits(ctx, uint64(1)).MaxTimes(1) - m.GovKeeper.EXPECT().SetProposal(ctx, proposalWithVeto).After(call1) - m.GovKeeper.EXPECT().DeleteVotes(ctx, uint64(1)).After(call1) - call2 := m.GovKeeper.EXPECT().RemoveFromActiveProposalQueue(ctx, uint64(1), votingEndTime).After(call1) - m.GovKeeper.EXPECT().UpdateMinInitialDeposit(ctx, true).After(call2) - m.GovKeeper.EXPECT().UpdateMinDeposit(ctx, true).After(call2) - }, + proposalState: "voting", setOversightDAO: true, + assertVetoed: true, }, { name: "proposal not in voting period", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 2, - }, - expectedErr: "proposal with ID 2 is not in voting period: inactive proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(2)).Return(depositPeriodProposal, true) - }, - setOversightDAO: true, - }, - { - name: "proposal already vetoed", - msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 3, - }, - expectedErr: "proposal with ID 3 is not in voting period: inactive proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(3)).Return(proposalWithVeto, true) + Vetoer: oversightDAOAcc, }, + expectedErr: "is not in voting period: inactive proposal", + proposalState: "deposit", setOversightDAO: true, }, { name: "veto proposal with change to oversight DAO address", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 4, - }, - expectedErr: "proposal with ID 4 contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(4)).Return(proposalWithChangeOversightDAO, true) + Vetoer: oversightDAOAcc, }, + expectedErr: "contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", + proposalState: "voting-change-oversight", setOversightDAO: true, }, { name: "veto proposal with disablement of oversight DAO", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 5, - }, - expectedErr: "proposal with ID 5 contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(5)).Return(proposalWithEmptyOversightDAO, true) + Vetoer: oversightDAOAcc, }, + expectedErr: "contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", + proposalState: "voting-disable-oversight", setOversightDAO: true, }, { name: "veto proposal with change to oversight DAO address wrapped in authz.MsgExec", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 6, - }, - expectedErr: "proposal with ID 6 contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(6)).Return(proposalWithWrappedChangeOversightDAO, true) + Vetoer: oversightDAOAcc, }, + expectedErr: "contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", + proposalState: "voting-change-oversight-wrapped", setOversightDAO: true, }, { name: "veto proposal with change to oversight DAO address double-wrapped in authz.MsgExec", msg: &types.MsgVetoProposal{ - Vetoer: oversightDAOAcc, - ProposalId: 7, - }, - expectedErr: "proposal with ID 7 contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", - setupMocks: func(ctx sdk.Context, m *testutil.Mocks) { - m.GovKeeper.EXPECT().GetProposal(ctx, uint64(7)).Return(proposalWithDoubleWrappedChangeOversightDAO, true) + Vetoer: oversightDAOAcc, }, + expectedErr: "contains a change of the oversight DAO address, vetoing it would prevent the replacement of the current oversight DAO: oversight DAO cannot veto this proposal", + proposalState: "voting-change-oversight-double-wrapped", setOversightDAO: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ms, k, m, ctx := testutil.SetupMsgServer(t) - tt.setupMocks(ctx, &m) + app := helpers.Setup(t) + ctx := app.NewUncachedContext(true, tmproto.Header{Time: time.Now()}) + ms := coredaoskeeper.NewMsgServer(app.CoreDaosKeeper) + params := types.DefaultParams() if tt.setOversightDAO { params.OversightDaoAddress = oversightDAOAcc } - require.NoError(t, k.Params.Set(ctx, params)) + require.NoError(t, app.CoreDaosKeeper.Params.Set(ctx, params)) + + govAddr := govModuleAddr() + // a MsgUpdateParams that changes the oversight DAO address. + changeOversightMsg := &types.MsgUpdateParams{ + Authority: govAddr, + Params: types.Params{ + OversightDaoAddress: newOversightAddr, + VotingPeriodExtensionDuration: &extDuration, + }, + } + // a MsgUpdateParams that disables (empties) the oversight DAO address. + disableOversightMsg := &types.MsgUpdateParams{ + Authority: govAddr, + Params: types.Params{ + OversightDaoAddress: "", + VotingPeriodExtensionDuration: &extDuration, + }, + } + + switch tt.proposalState { + case "voting": + p := submitBankSendProposalReal(t, app, ctx, true) + tt.msg.ProposalId = p.Id + case "deposit": + p := submitBankSendProposalReal(t, app, ctx, false) + tt.msg.ProposalId = p.Id + case "voting-change-oversight": + p := submitProposalReal(t, app, ctx, []sdk.Msg{changeOversightMsg}, true) + tt.msg.ProposalId = p.Id + case "voting-disable-oversight": + p := submitProposalReal(t, app, ctx, []sdk.Msg{disableOversightMsg}, true) + tt.msg.ProposalId = p.Id + case "voting-change-oversight-wrapped": + inner, err := codectypes.NewAnyWithValue(changeOversightMsg) + require.NoError(t, err) + exec := &authz.MsgExec{Grantee: govAddr, Msgs: []*codectypes.Any{inner}} + p := submitProposalReal(t, app, ctx, []sdk.Msg{exec}, true) + tt.msg.ProposalId = p.Id + case "voting-change-oversight-double-wrapped": + inner, err := codectypes.NewAnyWithValue(changeOversightMsg) + require.NoError(t, err) + execAny, err := codectypes.NewAnyWithValue(&authz.MsgExec{Grantee: govAddr, Msgs: []*codectypes.Any{inner}}) + require.NoError(t, err) + outer := &authz.MsgExec{Grantee: govAddr, Msgs: []*codectypes.Any{execAny}} + p := submitProposalReal(t, app, ctx, []sdk.Msg{outer}, true) + tt.msg.ProposalId = p.Id + } + if err := tt.msg.ValidateBasic(); err != nil { if tt.expectedErr != "" { require.EqualError(t, err, tt.expectedErr) @@ -1006,10 +933,25 @@ func TestMsgServerVetoProposal(t *testing.T) { } _, err := ms.VetoProposal(ctx, tt.msg) if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr) return } require.NoError(t, err) + if tt.assertVetoed { + got, err := app.GovKeeper.Proposals.Get(ctx, tt.msg.ProposalId) + require.NoError(t, err) + require.Equal(t, govv1.StatusVetoed, got.Status) + // final tally must be reset to empty + emptyTally := govv1.EmptyTallyResult() + require.Equal(t, &emptyTally, got.FinalTallyResult) + // voting ends immediately (set to block time) + require.WithinDuration(t, ctx.BlockTime(), *got.VotingEndTime, time.Second) + // proposal removed from active queue under its original end time + has, err := app.GovKeeper.ActiveProposalsQueue.Has(ctx, collectionsJoin(*got.VotingEndTime, got.Id)) + require.NoError(t, err) + require.False(t, has) + } }) } } diff --git a/x/coredaos/module.go b/x/coredaos/module.go index 3123bd1c..335e17e6 100644 --- a/x/coredaos/module.go +++ b/x/coredaos/module.go @@ -18,12 +18,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/atomone-hub/atomone/x/coredaos/client/cli" "github.com/atomone-hub/atomone/x/coredaos/keeper" "github.com/atomone-hub/atomone/x/coredaos/types" modulev1 "github.com/atomone-hub/atomone/x/coredaos/types/module" - govtypes "github.com/atomone-hub/atomone/x/gov/types" ) // ConsensusVersion is the x/coredaos module's consensus version identifier. @@ -105,7 +106,7 @@ type AppModule struct { AppModuleBasic keeper keeper.Keeper - govKeeper types.GovKeeper + govKeeper *govkeeper.Keeper stakingKeeper types.StakingKeeper accountKeeper types.AccountKeeper bankKeeper types.BankKeeper @@ -114,7 +115,7 @@ type AppModule struct { func NewAppModule( cdc codec.Codec, keeper keeper.Keeper, - gk types.GovKeeper, + gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, @@ -176,7 +177,7 @@ type Inputs struct { StoreService store.KVStoreService Cdc codec.Codec - GovKeeper types.GovKeeper + GovKeeper *govkeeper.Keeper StakingKeeper types.StakingKeeper AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper diff --git a/x/coredaos/simulation/operations.go b/x/coredaos/simulation/operations.go index 9856c897..7582a9b1 100644 --- a/x/coredaos/simulation/operations.go +++ b/x/coredaos/simulation/operations.go @@ -8,11 +8,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/atomone-hub/atomone/x/coredaos/keeper" "github.com/atomone-hub/atomone/x/coredaos/types" - govv1 "github.com/atomone-hub/atomone/x/gov/types/v1" ) var initialProposalID = uint64(100000000000000) @@ -40,7 +41,7 @@ const ( ) // WeightedOperations returns all the operations from the CoreDaos module with their respective weights -func WeightedOperations(appParams simtypes.AppParams, cdc codec.JSONCodec, gk types.GovKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simulation.WeightedOperations { +func WeightedOperations(appParams simtypes.AppParams, cdc codec.JSONCodec, gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simulation.WeightedOperations { var weightMsgAnnotateProposal int appParams.GetOrGenerate(OpWeightMsgAnnotateProposal, &weightMsgAnnotateProposal, nil, func(_ *rand.Rand) { @@ -89,7 +90,7 @@ func WeightedOperations(appParams simtypes.AppParams, cdc codec.JSONCodec, gk ty } } -func SimulateMsgAnnotateProposal(gk types.GovKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { +func SimulateMsgAnnotateProposal(gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { params := k.GetParams(ctx) @@ -128,7 +129,7 @@ func SimulateMsgAnnotateProposal(gk types.GovKeeper, sk types.StakingKeeper, ak } } -func SimulateMsgEndorseProposal(gk types.GovKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { +func SimulateMsgEndorseProposal(gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { params := k.GetParams(ctx) @@ -166,7 +167,7 @@ func SimulateMsgEndorseProposal(gk types.GovKeeper, sk types.StakingKeeper, ak t } } -func SimulateMsgExtendVotingPeriod(gk types.GovKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { +func SimulateMsgExtendVotingPeriod(gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { params := k.GetParams(ctx) @@ -221,7 +222,7 @@ func SimulateMsgExtendVotingPeriod(gk types.GovKeeper, sk types.StakingKeeper, a } } -func SimulateMsgVetoProposal(gk types.GovKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { +func SimulateMsgVetoProposal(gk *govkeeper.Keeper, sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { params := k.GetParams(ctx) @@ -283,8 +284,8 @@ func SimulateMsgVetoProposal(gk types.GovKeeper, sk types.StakingKeeper, ak type // (defined in gov GenesisState) and the latest proposal ID // that has voting period status // It does not provide a default ID. -func randomProposal(r *rand.Rand, k types.GovKeeper, ctx sdk.Context) (proposal govv1.Proposal, found bool) { - proposalID, _ := k.GetProposalID(ctx) +func randomProposal(r *rand.Rand, k *govkeeper.Keeper, ctx sdk.Context) (proposal govv1.Proposal, found bool) { + proposalID, _ := k.ProposalID.Peek(ctx) switch { case proposalID > initialProposalID: @@ -297,8 +298,8 @@ func randomProposal(r *rand.Rand, k types.GovKeeper, ctx sdk.Context) (proposal initialProposalID = proposalID } - proposal, ok := k.GetProposal(ctx, proposalID) - if !ok || proposal.Status != govv1.StatusVotingPeriod { + proposal, err := k.Proposals.Get(ctx, proposalID) + if err != nil || proposal.Status != govv1.StatusVotingPeriod { return proposal, false } diff --git a/x/coredaos/testutil/expected_keepers_mocks.go b/x/coredaos/testutil/expected_keepers_mocks.go index ac13d66e..3501dbc0 100644 --- a/x/coredaos/testutil/expected_keepers_mocks.go +++ b/x/coredaos/testutil/expected_keepers_mocks.go @@ -7,177 +7,12 @@ package testutil import ( context "context" reflect "reflect" - time "time" math "cosmossdk.io/math" - v1 "github.com/atomone-hub/atomone/x/gov/types/v1" types "github.com/cosmos/cosmos-sdk/types" gomock "github.com/golang/mock/gomock" ) -// MockGovKeeper is a mock of GovKeeper interface. -type MockGovKeeper struct { - ctrl *gomock.Controller - recorder *MockGovKeeperMockRecorder -} - -// MockGovKeeperMockRecorder is the mock recorder for MockGovKeeper. -type MockGovKeeperMockRecorder struct { - mock *MockGovKeeper -} - -// NewMockGovKeeper creates a new mock instance. -func NewMockGovKeeper(ctrl *gomock.Controller) *MockGovKeeper { - mock := &MockGovKeeper{ctrl: ctrl} - mock.recorder = &MockGovKeeperMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGovKeeper) EXPECT() *MockGovKeeperMockRecorder { - return m.recorder -} - -// DeleteAndBurnDeposits mocks base method. -func (m *MockGovKeeper) DeleteAndBurnDeposits(ctx types.Context, proposalID uint64) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteAndBurnDeposits", ctx, proposalID) -} - -// DeleteAndBurnDeposits indicates an expected call of DeleteAndBurnDeposits. -func (mr *MockGovKeeperMockRecorder) DeleteAndBurnDeposits(ctx, proposalID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAndBurnDeposits", reflect.TypeOf((*MockGovKeeper)(nil).DeleteAndBurnDeposits), ctx, proposalID) -} - -// DeleteVotes mocks base method. -func (m *MockGovKeeper) DeleteVotes(ctx types.Context, proposalID uint64) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "DeleteVotes", ctx, proposalID) -} - -// DeleteVotes indicates an expected call of DeleteVotes. -func (mr *MockGovKeeperMockRecorder) DeleteVotes(ctx, proposalID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVotes", reflect.TypeOf((*MockGovKeeper)(nil).DeleteVotes), ctx, proposalID) -} - -// GetProposal mocks base method. -func (m *MockGovKeeper) GetProposal(ctx types.Context, id uint64) (v1.Proposal, bool) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetProposal", ctx, id) - ret0, _ := ret[0].(v1.Proposal) - ret1, _ := ret[1].(bool) - return ret0, ret1 -} - -// GetProposal indicates an expected call of GetProposal. -func (mr *MockGovKeeperMockRecorder) GetProposal(ctx, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProposal", reflect.TypeOf((*MockGovKeeper)(nil).GetProposal), ctx, id) -} - -// GetProposalID mocks base method. -func (m *MockGovKeeper) GetProposalID(ctx types.Context) (uint64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetProposalID", ctx) - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetProposalID indicates an expected call of GetProposalID. -func (mr *MockGovKeeperMockRecorder) GetProposalID(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProposalID", reflect.TypeOf((*MockGovKeeper)(nil).GetProposalID), ctx) -} - -// InsertActiveProposalQueue mocks base method. -func (m *MockGovKeeper) InsertActiveProposalQueue(ctx types.Context, proposalID uint64, endTime time.Time) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "InsertActiveProposalQueue", ctx, proposalID, endTime) -} - -// InsertActiveProposalQueue indicates an expected call of InsertActiveProposalQueue. -func (mr *MockGovKeeperMockRecorder) InsertActiveProposalQueue(ctx, proposalID, endTime interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertActiveProposalQueue", reflect.TypeOf((*MockGovKeeper)(nil).InsertActiveProposalQueue), ctx, proposalID, endTime) -} - -// ProposalKinds mocks base method. -func (m *MockGovKeeper) ProposalKinds(proposal v1.Proposal) v1.ProposalKinds { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProposalKinds", proposal) - ret0, _ := ret[0].(v1.ProposalKinds) - return ret0 -} - -// ProposalKinds indicates an expected call of ProposalKinds. -func (mr *MockGovKeeperMockRecorder) ProposalKinds(proposal interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposalKinds", reflect.TypeOf((*MockGovKeeper)(nil).ProposalKinds), proposal) -} - -// RefundAndDeleteDeposits mocks base method. -func (m *MockGovKeeper) RefundAndDeleteDeposits(ctx types.Context, proposalID uint64) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RefundAndDeleteDeposits", ctx, proposalID) -} - -// RefundAndDeleteDeposits indicates an expected call of RefundAndDeleteDeposits. -func (mr *MockGovKeeperMockRecorder) RefundAndDeleteDeposits(ctx, proposalID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefundAndDeleteDeposits", reflect.TypeOf((*MockGovKeeper)(nil).RefundAndDeleteDeposits), ctx, proposalID) -} - -// RemoveFromActiveProposalQueue mocks base method. -func (m *MockGovKeeper) RemoveFromActiveProposalQueue(ctx types.Context, proposalID uint64, endTime time.Time) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RemoveFromActiveProposalQueue", ctx, proposalID, endTime) -} - -// RemoveFromActiveProposalQueue indicates an expected call of RemoveFromActiveProposalQueue. -func (mr *MockGovKeeperMockRecorder) RemoveFromActiveProposalQueue(ctx, proposalID, endTime interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFromActiveProposalQueue", reflect.TypeOf((*MockGovKeeper)(nil).RemoveFromActiveProposalQueue), ctx, proposalID, endTime) -} - -// SetProposal mocks base method. -func (m *MockGovKeeper) SetProposal(ctx types.Context, proposal v1.Proposal) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetProposal", ctx, proposal) -} - -// SetProposal indicates an expected call of SetProposal. -func (mr *MockGovKeeperMockRecorder) SetProposal(ctx, proposal interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProposal", reflect.TypeOf((*MockGovKeeper)(nil).SetProposal), ctx, proposal) -} - -// UpdateMinDeposit mocks base method. -func (m *MockGovKeeper) UpdateMinDeposit(ctx types.Context, checkElapsedTime bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UpdateMinDeposit", ctx, checkElapsedTime) -} - -// UpdateMinDeposit indicates an expected call of UpdateMinDeposit. -func (mr *MockGovKeeperMockRecorder) UpdateMinDeposit(ctx, checkElapsedTime interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMinDeposit", reflect.TypeOf((*MockGovKeeper)(nil).UpdateMinDeposit), ctx, checkElapsedTime) -} - -// UpdateMinInitialDeposit mocks base method. -func (m *MockGovKeeper) UpdateMinInitialDeposit(ctx types.Context, checkElapsedTime bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "UpdateMinInitialDeposit", ctx, checkElapsedTime) -} - -// UpdateMinInitialDeposit indicates an expected call of UpdateMinInitialDeposit. -func (mr *MockGovKeeperMockRecorder) UpdateMinInitialDeposit(ctx, checkElapsedTime interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMinInitialDeposit", reflect.TypeOf((*MockGovKeeper)(nil).UpdateMinInitialDeposit), ctx, checkElapsedTime) -} - // MockStakingKeeper is a mock of StakingKeeper interface. type MockStakingKeeper struct { ctrl *gomock.Controller diff --git a/x/coredaos/testutil/keeper.go b/x/coredaos/testutil/keeper.go index 10e1804c..0536ebd2 100644 --- a/x/coredaos/testutil/keeper.go +++ b/x/coredaos/testutil/keeper.go @@ -16,14 +16,13 @@ import ( moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/atomone-hub/atomone/x/coredaos/keeper" "github.com/atomone-hub/atomone/x/coredaos/types" - govtypes "github.com/atomone-hub/atomone/x/gov/types" ) type Mocks struct { - GovKeeper *MockGovKeeper StakingKeeper *MockStakingKeeper } @@ -41,7 +40,6 @@ func SetupCoredaosKeeper(t *testing.T) ( t.Helper() ctrl := gomock.NewController(t) m := Mocks{ - GovKeeper: NewMockGovKeeper(ctrl), StakingKeeper: NewMockStakingKeeper(ctrl), } @@ -53,5 +51,8 @@ func SetupCoredaosKeeper(t *testing.T) ( types.RegisterInterfaces(encCfg.InterfaceRegistry) authz.RegisterInterfaces(encCfg.InterfaceRegistry) authority := authtypes.NewModuleAddress(govtypes.ModuleName).String() - return keeper.NewKeeper(encCfg.Codec, storeService, authority, m.GovKeeper, m.StakingKeeper), m, ctx + // The gov keeper is not exercised by tests that use this lightweight + // harness (e.g. UpdateParams), so a nil gov keeper is sufficient here. + // Tests that interact with gov use the full app via helpers.Setup. + return keeper.NewKeeper(encCfg.Codec, storeService, authority, nil, m.StakingKeeper), m, ctx } diff --git a/x/coredaos/types/errors.go b/x/coredaos/types/errors.go index bea16bf2..19ffc3aa 100644 --- a/x/coredaos/types/errors.go +++ b/x/coredaos/types/errors.go @@ -12,4 +12,5 @@ var ( ErrFunctionDisabled = errorsmod.Register(ModuleName, 4, "function is disabled") ErrCannotStake = errorsmod.Register(ModuleName, 5, "core DAOs cannot stake") ErrInvalidVeto = errorsmod.Register(ModuleName, 6, "oversight DAO cannot veto this proposal") + ErrUnknownProposal = errorsmod.Register(ModuleName, 7, "unknown proposal") ) diff --git a/x/coredaos/types/expected_keepers.go b/x/coredaos/types/expected_keepers.go index f6aaa2e7..1a373e8b 100644 --- a/x/coredaos/types/expected_keepers.go +++ b/x/coredaos/types/expected_keepers.go @@ -2,49 +2,12 @@ package types import ( "context" - time "time" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - - govtypesv1 "github.com/atomone-hub/atomone/x/gov/types/v1" ) -// GovKeeper defines the expected interface needed to interact with the -// governance module. -type GovKeeper interface { - // GetProposalID gets the highest proposal ID - GetProposalID(ctx sdk.Context) (proposalID uint64, err error) - // GetProposal gets a proposal from store by ProposalID. - GetProposal(ctx sdk.Context, id uint64) (govtypesv1.Proposal, bool) - // SetProposal sets a proposal in the gov store. - SetProposal(ctx sdk.Context, proposal govtypesv1.Proposal) - // ProposalKinds returns a v1.ProposalKinds useful to determine which kind - // of messages are included in a proposal. - ProposalKinds(proposal govtypesv1.Proposal) govtypesv1.ProposalKinds - // DeleteAndBurnDeposits deletes and burns all the deposits on a - // specific proposal. - DeleteAndBurnDeposits(ctx sdk.Context, proposalID uint64) - // RefundAndDeleteDeposits refunds and deletes all the deposits on a - // specific proposal. - RefundAndDeleteDeposits(ctx sdk.Context, proposalID uint64) - // InsertActiveProposalQueue inserts a proposalID into the active proposal - // queue at endTime - InsertActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) - // RemoveFromActiveProposalQueue removes a proposalID from the Active - // Proposal Queue - RemoveFromActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) - // UpdateMinInitialDeposit updates the min initial deposit required for - // proposal submission - UpdateMinInitialDeposit(ctx sdk.Context, checkElapsedTime bool) - // UpdateMinDeposit updates the minimum deposit required for a proposal - UpdateMinDeposit(ctx sdk.Context, checkElapsedTime bool) - - // DeleteVotes deletes all votes from a proposal with given proposalID - DeleteVotes(ctx sdk.Context, proposalID uint64) -} - // StakingKeeper defines the expected interface needed to interact with the // staking module. type StakingKeeper interface {