diff --git a/protocol/testing/e2e/gov/add_new_market_test.go b/protocol/testing/e2e/gov/add_new_market_test.go index e26522663d..a08ab1431f 100644 --- a/protocol/testing/e2e/gov/add_new_market_test.go +++ b/protocol/testing/e2e/gov/add_new_market_test.go @@ -62,6 +62,7 @@ func TestAddNewMarketProposal(t *testing.T) { tests := map[string]struct { proposedMsgs []sdk.Msg updateClobDelayBlocks uint32 + expectCheckTxFails bool expectSubmitProposalFail bool expectDelayedUpdateClobPairMsgFailure bool expectedProposalStatus govtypesv1.ProposalStatus @@ -284,6 +285,7 @@ func TestAddNewMarketProposal(t *testing.T) { ctx, tApp, tc.proposedMsgs, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) diff --git a/protocol/testing/e2e/gov/bridge_test.go b/protocol/testing/e2e/gov/bridge_test.go index 22e3fcdb16..24cab9cd67 100644 --- a/protocol/testing/e2e/gov/bridge_test.go +++ b/protocol/testing/e2e/gov/bridge_test.go @@ -18,6 +18,7 @@ func TestUpdateEventParams(t *testing.T) { tests := map[string]struct { msg *bridgetypes.MsgUpdateEventParams + expectCheckTxFails bool expectSubmitProposalFail bool expectedProposalStatus govtypesv1.ProposalStatus }{ @@ -65,6 +66,7 @@ func TestUpdateEventParams(t *testing.T) { ctx, tApp, []sdk.Msg{tc.msg}, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) @@ -83,6 +85,7 @@ func TestUpdateProposeParams(t *testing.T) { tests := map[string]struct { msg *bridgetypes.MsgUpdateProposeParams + expectCheckTxFails bool expectSubmitProposalFail bool expectedProposalStatus govtypesv1.ProposalStatus }{ @@ -132,6 +135,7 @@ func TestUpdateProposeParams(t *testing.T) { ctx, tApp, []sdk.Msg{tc.msg}, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) @@ -150,6 +154,7 @@ func TestUpdateSafetyParams(t *testing.T) { tests := map[string]struct { msg *bridgetypes.MsgUpdateSafetyParams + expectCheckTxFails bool expectSubmitProposalFail bool expectedProposalStatus govtypesv1.ProposalStatus }{ @@ -195,6 +200,7 @@ func TestUpdateSafetyParams(t *testing.T) { ctx, tApp, []sdk.Msg{tc.msg}, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) diff --git a/protocol/testing/e2e/gov/sending_test.go b/protocol/testing/e2e/gov/sending_test.go index 21bc31adec..07f9318c59 100644 --- a/protocol/testing/e2e/gov/sending_test.go +++ b/protocol/testing/e2e/gov/sending_test.go @@ -20,6 +20,7 @@ func TestSendFromModuleToAccount(t *testing.T) { tests := map[string]struct { msg *sendingtypes.MsgSendFromModuleToAccount initialModuleBalance int64 + expectCheckTxFails bool expectedProposalStatus govtypesv1.ProposalStatus expectSubmitProposalFail bool }{ @@ -104,6 +105,7 @@ func TestSendFromModuleToAccount(t *testing.T) { ctx, tApp, []sdk.Msg{tc.msg}, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) diff --git a/protocol/testing/e2e/gov/stats_test.go b/protocol/testing/e2e/gov/stats_test.go index c8d16ead92..5debce0c5a 100644 --- a/protocol/testing/e2e/gov/stats_test.go +++ b/protocol/testing/e2e/gov/stats_test.go @@ -17,6 +17,7 @@ import ( func TestUpdateParams(t *testing.T) { tests := map[string]struct { msg *statstypes.MsgUpdateParams + expectCheckTxFails bool expectedProposalStatus govtypesv1.ProposalStatus expectSubmitProposalFail bool }{ @@ -70,6 +71,7 @@ func TestUpdateParams(t *testing.T) { ctx, tApp, []sdk.Msg{tc.msg}, + tc.expectCheckTxFails, tc.expectSubmitProposalFail, tc.expectedProposalStatus, ) diff --git a/protocol/testing/e2e/gov/vest_test.go b/protocol/testing/e2e/gov/vest_test.go new file mode 100644 index 0000000000..4161b1230f --- /dev/null +++ b/protocol/testing/e2e/gov/vest_test.go @@ -0,0 +1,501 @@ +package gov_test + +import ( + "testing" + "time" + + "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + vesttypes "github.com/dydxprotocol/v4-chain/protocol/x/vest/types" + "github.com/stretchr/testify/require" +) + +var ( + TEST_START_TIME = time.Date(2023, 10, 2, 0, 0, 0, 0, time.UTC) + TEST_END_TIME = time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC) + TEST_VESTER_ACCOUNT_1 = "random_vester" + TEST_VESTER_ACCOUNT_2 = "random_vester_2" + TEST_GENESIS_VEST_ENTRY = vesttypes.VestEntry{ + VesterAccount: "genesis_vester", + TreasuryAccount: "genesis_treasury", + Denom: "genesis_denom", + StartTime: TEST_START_TIME.AddDate(-2022, -1, -1), + EndTime: TEST_END_TIME.AddDate(-2022, -1, -1), + } +) + +func TestSetVestEntry_Success(t *testing.T) { + tests := map[string]struct { + msgs []sdk.Msg + genesisVestEntryKeys []string // keys of vest entries in genesis state. + }{ + "Success: create a new vest entry": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + }, + "Success: update an existing vest entry": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + }, + "Success: create two new vest entries": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_2, + TreasuryAccount: "random_treasury_2", + Denom: "adv4tnt", + StartTime: TEST_START_TIME.Add(time.Hour), + EndTime: TEST_END_TIME.Add(time.Hour), + }, + }, + }, + }, + "Success: create and then update a vest entry": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury_2", + Denom: "adv4tnt", + StartTime: TEST_START_TIME.Add(time.Hour), + EndTime: TEST_END_TIME.Add(time.Hour), + }, + }, + }, + }, + "Success: update a vest entry twice": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury_2", + Denom: "adv4tnt", + StartTime: TEST_START_TIME.Add(time.Hour), + EndTime: TEST_END_TIME.Add(time.Hour), + }, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *govtypesv1.GenesisState) { + genesisState.Params.VotingPeriod = &testapp.TestVotingPeriod + }, + ) + // Set vest module genesis state with vest entries. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *vesttypes.GenesisState) { + genesisState.VestEntries = make([]vesttypes.VestEntry, len(tc.genesisVestEntryKeys)) + for i, key := range tc.genesisVestEntryKeys { + genesisState.VestEntries[i] = vesttypes.VestEntry{ + VesterAccount: key, + TreasuryAccount: TEST_GENESIS_VEST_ENTRY.TreasuryAccount, + Denom: TEST_GENESIS_VEST_ENTRY.Denom, + StartTime: TEST_GENESIS_VEST_ENTRY.StartTime, + EndTime: TEST_GENESIS_VEST_ENTRY.EndTime, + } + } + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + + // Submit and tally governance proposal that includes `MsgSetVestEntry`s. + ctx = testapp.SubmitAndTallyProposal( + t, + ctx, + tApp, + tc.msgs, + false, // checkTx should not fail. + false, // submitProposal should not fail. + govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED, + ) + + // Verify that expected vest entries are set in state. + finalVestEntries := make(map[string]vesttypes.VestEntry) + for _, msg := range tc.msgs { + msgSetVestEntry, ok := msg.(*vesttypes.MsgSetVestEntry) + require.True(t, ok) + finalVestEntries[msgSetVestEntry.Entry.VesterAccount] = msgSetVestEntry.Entry + } + for vesterAccount, expectedVestEntry := range finalVestEntries { + vestEntryInState, err := tApp.App.VestKeeper.GetVestEntry(ctx, vesterAccount) + require.NoError(t, err) + require.Equal(t, expectedVestEntry, vestEntryInState) + } + }) + } +} + +func TestSetVestEntry_Failure(t *testing.T) { + tests := map[string]struct { + msgs []sdk.Msg + expectCheckTxFails bool + expectSubmitProposalFail bool + }{ + "Failure: vester account is empty": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: "", + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + expectCheckTxFails: true, + }, + "Failure: treasury account is empty": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + expectCheckTxFails: true, + }, + "Failure: start time after end time": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "", + Denom: "adv4tnt", + StartTime: TEST_END_TIME, + EndTime: TEST_START_TIME, + }, + }, + }, + expectCheckTxFails: true, + }, + "Failure: invalid authority": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ + Authority: constants.BobAccAddress.String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + expectSubmitProposalFail: true, + }, + "Failure: failure of one message causes rollback of others": { + msgs: []sdk.Msg{ + &vesttypes.MsgSetVestEntry{ // Valid message. + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + &vesttypes.MsgSetVestEntry{ // Invalid message (due to invalid authority). + Authority: constants.BobAccAddress.String(), + Entry: vesttypes.VestEntry{ + VesterAccount: TEST_VESTER_ACCOUNT_1, + TreasuryAccount: "random_treasury", + Denom: "adv4tnt", + StartTime: TEST_START_TIME, + EndTime: TEST_END_TIME, + }, + }, + }, + expectSubmitProposalFail: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *govtypesv1.GenesisState) { + genesisState.Params.VotingPeriod = &testapp.TestVotingPeriod + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + initialVestEntries := tApp.App.VestKeeper.GetAllVestEntries(ctx) + + // Submit and tally governance proposal that includes `MsgSetVestEntry`s. + ctx = testapp.SubmitAndTallyProposal( + t, + ctx, + tApp, + tc.msgs, + tc.expectCheckTxFails, + tc.expectSubmitProposalFail, + govtypesv1.ProposalStatus_PROPOSAL_STATUS_FAILED, + ) + + // Verify that vest entries in state match the ones before proposal submission. + require.Equal(t, initialVestEntries, tApp.App.VestKeeper.GetAllVestEntries(ctx)) + }) + } +} + +func TestDeleteVestEntry_Success(t *testing.T) { + tests := map[string]struct { + msgs []sdk.Msg + genesisVestEntryKeys []string // keys of vest entries in genesis state. + }{ + "Success: delete one vest entry": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + }, + "Success: delete two vest entries": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_2, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1, TEST_VESTER_ACCOUNT_2}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *govtypesv1.GenesisState) { + genesisState.Params.VotingPeriod = &testapp.TestVotingPeriod + }, + ) + // Set vest module genesis state with vest entries. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *vesttypes.GenesisState) { + genesisState.VestEntries = make([]vesttypes.VestEntry, len(tc.genesisVestEntryKeys)) + for i, key := range tc.genesisVestEntryKeys { + genesisState.VestEntries[i] = vesttypes.VestEntry{ + VesterAccount: key, + TreasuryAccount: TEST_GENESIS_VEST_ENTRY.TreasuryAccount, + Denom: TEST_GENESIS_VEST_ENTRY.Denom, + StartTime: TEST_GENESIS_VEST_ENTRY.StartTime, + EndTime: TEST_GENESIS_VEST_ENTRY.EndTime, + } + } + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + + // Submit and tally governance proposal that includes `MsgDeleteVestEntry`s. + ctx = testapp.SubmitAndTallyProposal( + t, + ctx, + tApp, + tc.msgs, + false, // checkTx should not fail. + false, // submitProposal should not fail. + govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED, + ) + + // Verify that vest entries have been deleted. + for _, msg := range tc.msgs { + vestEntry, err := tApp.App.VestKeeper.GetVestEntry(ctx, msg.(*vesttypes.MsgDeleteVestEntry).VesterAccount) + require.Equal(t, vesttypes.VestEntry{}, vestEntry) + require.Error(t, err) + } + }) + } +} + +func TestDeleteVestEntry_Failure(t *testing.T) { + tests := map[string]struct { + msgs []sdk.Msg + genesisVestEntryKeys []string // key of vest entries in genesis state. + expectSubmitProposalFail bool + }{ + "Failure: vest entry does not exist": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + }, + }, + "Failure: delete the same vest entry twice": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + }, + "Failure: second vest entry to delete does not exist": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + &vesttypes.MsgDeleteVestEntry{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + VesterAccount: TEST_VESTER_ACCOUNT_2, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + }, + "Failure: invalid authority": { + msgs: []sdk.Msg{ + &vesttypes.MsgDeleteVestEntry{ + Authority: constants.BobAccAddress.String(), + VesterAccount: TEST_VESTER_ACCOUNT_1, + }, + }, + genesisVestEntryKeys: []string{TEST_VESTER_ACCOUNT_1}, + expectSubmitProposalFail: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *govtypesv1.GenesisState) { + genesisState.Params.VotingPeriod = &testapp.TestVotingPeriod + }, + ) + // Set vest module genesis state with vest entries. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *vesttypes.GenesisState) { + genesisState.VestEntries = make([]vesttypes.VestEntry, len(tc.genesisVestEntryKeys)) + for i, key := range tc.genesisVestEntryKeys { + genesisState.VestEntries[i] = vesttypes.VestEntry{ + VesterAccount: key, + TreasuryAccount: TEST_GENESIS_VEST_ENTRY.TreasuryAccount, + Denom: TEST_GENESIS_VEST_ENTRY.Denom, + StartTime: TEST_GENESIS_VEST_ENTRY.StartTime, + EndTime: TEST_GENESIS_VEST_ENTRY.EndTime, + } + } + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + initialVestEntries := tApp.App.VestKeeper.GetAllVestEntries(ctx) + + // Submit and tally governance proposal that includes `MsgDeleteVestEntry`s. + ctx = testapp.SubmitAndTallyProposal( + t, + ctx, + tApp, + tc.msgs, + false, // checkTx should not fail. + tc.expectSubmitProposalFail, + govtypesv1.ProposalStatus_PROPOSAL_STATUS_FAILED, + ) + + // Verify that vest entries in state match vest entries before proposal submission. + require.Equal(t, initialVestEntries, tApp.App.VestKeeper.GetAllVestEntries(ctx)) + }) + } +} diff --git a/protocol/testutil/app/gov.go b/protocol/testutil/app/gov.go index 505abae336..81c0c66813 100644 --- a/protocol/testutil/app/gov.go +++ b/protocol/testutil/app/gov.go @@ -35,6 +35,7 @@ func SubmitAndTallyProposal( ctx sdk.Context, tApp *TestApp, messages []sdk.Msg, + expectCheckTxFails bool, expectSubmitProposalFails bool, expectedProposalStatus govtypesv1.ProposalStatus, ) sdk.Context { @@ -61,7 +62,13 @@ func SubmitAndTallyProposal( constants.GetPrivateKeyFromAddress, msgSubmitProposal, ) - require.True(t, tApp.CheckTx(submitProposalCheckTx).IsOK()) + if expectCheckTxFails { + require.False(t, tApp.CheckTx(submitProposalCheckTx).IsOK()) + // CheckTx failed. Return early. + return ctx + } else { + require.True(t, tApp.CheckTx(submitProposalCheckTx).IsOK()) + } if expectSubmitProposalFails { ctx = tApp.AdvanceToBlock(TestSubmitProposalTxHeight, AdvanceToBlockOptions{