From 973dbee2be9b5cd8f2313739930f7418db647e2a Mon Sep 17 00:00:00 2001 From: stana-ethernal Date: Thu, 21 Nov 2024 17:42:08 +0100 Subject: [PATCH] additional tests --- .../keeper/infraction_parameters_test.go | 216 ++++++++++++++++++ x/ccv/provider/keeper/msg_server_test.go | 62 ++++- 2 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 x/ccv/provider/keeper/infraction_parameters_test.go diff --git a/x/ccv/provider/keeper/infraction_parameters_test.go b/x/ccv/provider/keeper/infraction_parameters_test.go new file mode 100644 index 0000000000..11168c03a1 --- /dev/null +++ b/x/ccv/provider/keeper/infraction_parameters_test.go @@ -0,0 +1,216 @@ +package keeper_test + +import ( + "testing" + "time" + + "cosmossdk.io/math" + testkeeper "github.com/cosmos/interchain-security/v6/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func TestUpdateQueuedInfractionParams_RemoveIdenticalItem(t *testing.T) { + k, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerID := "consumer1" + initialParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + } + + // Set initial infraction parameters + require.NoError(t, k.SetInfractionParameters(ctx, consumerID, initialParams)) + + // Queue identical parameters + require.NoError(t, k.UpdateQueuedInfractionParams(ctx, consumerID, initialParams)) + + // Verify queue is empty + hasQueued := k.HasQueuedInfractionParameters(ctx, consumerID) + require.False(t, hasQueued) +} + +func TestUpdateQueuedInfractionParams_UpdateDifferentItem(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + mocks.MockStakingKeeper.EXPECT().UnbondingTime(ctx).Return(time.Second, nil).AnyTimes() + + consumerID := "consumer2" + initialParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + } + newParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 2000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(5, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDec(1), + }, + } + + // Set initial infraction parameters + require.NoError(t, k.SetInfractionParameters(ctx, consumerID, initialParams)) + + // Queue different parameters + require.NoError(t, k.UpdateQueuedInfractionParams(ctx, consumerID, newParams)) + + // Verify queue has updated parameters + queuedParams, err := k.GetQueuedInfractionParameters(ctx, consumerID) + require.NoError(t, err) + require.Equal(t, newParams, queuedParams) +} + +func TestUpdateQueuedInfractionParams_OnlyLastItemInQueue(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + mocks.MockStakingKeeper.EXPECT().UnbondingTime(ctx).Return(time.Second, nil).AnyTimes() + + initialParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + } + + consumerID := "consumer3" + paramsList := []providertypes.InfractionParameters{ + { + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + }, + { + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1500 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(3, 1), + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 600 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + }, + } + + // Set initial infraction parameters + require.NoError(t, k.SetInfractionParameters(ctx, consumerID, initialParams)) + + // Queue multiple parameters + for _, params := range paramsList { + require.NoError(t, k.UpdateQueuedInfractionParams(ctx, consumerID, params)) + } + + // Verify only the last one is kept in the queue + queuedParams, err := k.GetQueuedInfractionParameters(ctx, consumerID) + require.NoError(t, err) + require.Equal(t, paramsList[len(paramsList)-1], queuedParams) +} + +func TestBeginBlockUpdateInfractionParameters_MixedTiming(t *testing.T) { + keeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + unbondingTime := time.Second + mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Return(unbondingTime, nil).AnyTimes() + + // Define old and new InfractionParameters + oldInfractionParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), // 0.4 + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + } + newInfractionParams := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1200 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(5, 1), // 0.5 + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 600 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(1, 1), // 0.1 + }, + } + + // Define consumers + consumerIds := []string{"consumer1", "consumer2", "consumer3", "consumer4"} + + // Set old infraction parameters for all consumers + for _, consumerId := range consumerIds { + err := keeper.SetInfractionParameters(ctx, consumerId, oldInfractionParams) + require.NoError(t, err) + } + + // Create contexts for different time scenarios + currentTime := ctx.BlockTime() + ctxWithTimeBefore := ctx.WithBlockTime(currentTime.Add(-2 * unbondingTime)) + ctxWithTimeAfter := ctx.WithBlockTime(currentTime.Add(2 * unbondingTime)) + + // Queue updates for consumers + err := keeper.UpdateQueuedInfractionParams(ctxWithTimeBefore, "consumer1", newInfractionParams) + require.NoError(t, err) + err = keeper.UpdateQueuedInfractionParams(ctxWithTimeBefore, "consumer2", newInfractionParams) + require.NoError(t, err) + err = keeper.UpdateQueuedInfractionParams(ctxWithTimeAfter, "consumer3", newInfractionParams) + require.NoError(t, err) + err = keeper.UpdateQueuedInfractionParams(ctxWithTimeAfter, "consumer4", newInfractionParams) + require.NoError(t, err) + + // Call BeginBlockUpdateInfractionParameters with current context + err = keeper.BeginBlockUpdateInfractionParameters(ctx) + require.NoError(t, err) + + // Confirm queue state + require.True(t, keeper.HasQueuedInfractionParameters(ctx, "consumer3")) + require.True(t, keeper.HasQueuedInfractionParameters(ctx, "consumer4")) + require.False(t, keeper.HasQueuedInfractionParameters(ctx, "consumer1")) + require.False(t, keeper.HasQueuedInfractionParameters(ctx, "consumer2")) + + // Confirm infraction parameters are updated for consumer1 and consumer2 + params1, err := keeper.GetInfractionParameters(ctx, "consumer1") + require.NoError(t, err) + require.Equal(t, params1, newInfractionParams) + + params2, err := keeper.GetInfractionParameters(ctx, "consumer2") + require.NoError(t, err) + require.Equal(t, params2, newInfractionParams) + + // Confirm infraction parameters are not updated for consumer3 and consumer4 + params3, err := keeper.GetInfractionParameters(ctx, "consumer3") + require.NoError(t, err) + require.Equal(t, params3, oldInfractionParams) + + params4, err := keeper.GetInfractionParameters(ctx, "consumer4") + require.NoError(t, err) + require.Equal(t, params4, oldInfractionParams) +} diff --git a/x/ccv/provider/keeper/msg_server_test.go b/x/ccv/provider/keeper/msg_server_test.go index 9e60ab484f..953fecd0f7 100644 --- a/x/ccv/provider/keeper/msg_server_test.go +++ b/x/ccv/provider/keeper/msg_server_test.go @@ -4,7 +4,9 @@ import ( "testing" "time" + "cosmossdk.io/math" "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -16,9 +18,12 @@ import ( ) func TestCreateConsumer(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + mocks.MockSlashingKeeper.EXPECT().DowntimeJailDuration(gomock.Any()).Return(time.Second*600, nil).AnyTimes() + mocks.MockSlashingKeeper.EXPECT().SlashFractionDoubleSign(gomock.Any()).Return(math.LegacyNewDec(0), nil).AnyTimes() + msgServer := providerkeeper.NewMsgServerImpl(&providerKeeper) consumerMetadata := providertypes.ConsumerMetadata{ @@ -41,6 +46,11 @@ func TestCreateConsumer(t *testing.T) { require.Equal(t, "submitter", ownerAddress) phase := providerKeeper.GetConsumerPhase(ctx, "0") require.Equal(t, providertypes.CONSUMER_PHASE_REGISTERED, phase) + infractionParam, err := providerKeeper.GetInfractionParameters(ctx, response.ConsumerId) + require.NoError(t, err) + expectedInfractionParameters, err := providertypes.DefaultConsumerInfractionParameters(ctx, mocks.MockSlashingKeeper) + require.NoError(t, err) + require.Equal(t, expectedInfractionParameters, infractionParam) consumerMetadata = providertypes.ConsumerMetadata{ Name: "chain name", @@ -69,6 +79,11 @@ func TestUpdateConsumer(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + mocks.MockSlashingKeeper.EXPECT().DowntimeJailDuration(gomock.Any()).Return(time.Second*600, nil).AnyTimes() + mocks.MockSlashingKeeper.EXPECT().SlashFractionDoubleSign(gomock.Any()).Return(math.LegacyNewDec(0), nil).AnyTimes() + unbondingTime := 2 * time.Second + mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Return(unbondingTime, nil).AnyTimes() + msgServer := providerkeeper.NewMsgServerImpl(&providerKeeper) // try to update a non-existing (i.e., no consumer id exists) @@ -142,6 +157,16 @@ func TestUpdateConsumer(t *testing.T) { expectedInitializationParameters := testkeeper.GetTestInitializationParameters() expectedInitializationParameters.InitialHeight.RevisionNumber = 1 expectedPowerShapingParameters := testkeeper.GetTestPowerShapingParameters() + expectedInfractionParameters := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(4, 1), // 0.4 + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 500 * time.Second, + SlashFraction: math.LegacyNewDec(0), + }, + } expectedOwnerAddress := "cosmos1dkas8mu4kyhl5jrh4nzvm65qz588hy9qcz08la" expectedChainId = "updatedChainId-1" @@ -151,6 +176,7 @@ func TestUpdateConsumer(t *testing.T) { Metadata: &expectedConsumerMetadata, InitializationParameters: &expectedInitializationParameters, PowerShapingParameters: &expectedPowerShapingParameters, + InfractionParameters: &expectedInfractionParameters, NewChainId: expectedChainId, }) require.NoError(t, err) @@ -175,6 +201,11 @@ func TestUpdateConsumer(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedPowerShapingParameters, actualPowerShapingParameters) + // assert that infraction parameters were updated + actualInfractionParameters, err := providerKeeper.GetInfractionParameters(ctx, consumerId) + require.NoError(t, err) + require.Equal(t, expectedInfractionParameters, actualInfractionParameters) + // assert that the chain id has been updated actualChainId, err := providerKeeper.GetConsumerChainId(ctx, consumerId) require.NoError(t, err) @@ -282,6 +313,35 @@ func TestUpdateConsumer(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedPowerShapingParameters, actualPowerShapingParameters) + // assert that we can update the infraction parameters of a launched chain + providerKeeper.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + newExpectedInfractionParameters := providertypes.InfractionParameters{ + DoubleSign: &providertypes.SlashJailParameters{ + JailDuration: 2000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(6, 1), // 0.6 + }, + Downtime: &providertypes.SlashJailParameters{ + JailDuration: 1000 * time.Second, + SlashFraction: math.LegacyNewDecWithPrec(2, 1), + }, + } + _, err = msgServer.UpdateConsumer(ctx, + &providertypes.MsgUpdateConsumer{ + Owner: expectedOwnerAddress, ConsumerId: consumerId, + Metadata: nil, + InitializationParameters: nil, + PowerShapingParameters: nil, + InfractionParameters: &newExpectedInfractionParameters, + }) + require.NoError(t, err) + // infraction parameters are queued and not updated yet to newExpectedInfractionParameters since the chain is launched + require.Equal(t, expectedInfractionParameters, actualInfractionParameters) + // trigger update of queud infraction params after unbonding time is passed + providerKeeper.BeginBlockUpdateInfractionParameters(ctx.WithBlockTime(ctx.BlockTime().Add(2 * unbondingTime))) + actualInfractionParameters, err = providerKeeper.GetInfractionParameters(ctx, consumerId) + require.NoError(t, err) + require.Equal(t, newExpectedInfractionParameters, actualInfractionParameters) + // assert that if we call `MsgUpdateConsumer` with a spawn time of zero on an initialized chain, the chain // will not be scheduled to launch and will move back to its Registered phase providerKeeper.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_INITIALIZED)