diff --git a/x/bundles/keeper/keeper_suite_zero_delegation_test.go b/x/bundles/keeper/keeper_suite_zero_delegation_test.go index f087c71f..eca7fa70 100644 --- a/x/bundles/keeper/keeper_suite_zero_delegation_test.go +++ b/x/bundles/keeper/keeper_suite_zero_delegation_test.go @@ -2,7 +2,6 @@ package keeper_test import ( "cosmossdk.io/math" - "fmt" i "github.com/KYVENetwork/chain/testutil/integration" bundletypes "github.com/KYVENetwork/chain/x/bundles/types" funderstypes "github.com/KYVENetwork/chain/x/funders/types" @@ -165,17 +164,8 @@ var _ = Describe("zero delegation", Ordered, func() { Expect(bundleProposal.VotersInvalid).NotTo(ContainElement(i.STAKER_2)) Expect(bundleProposal.VotersAbstain).NotTo(ContainElement(i.STAKER_2)) - fmt.Println(bundleProposal) - effectiveStakes := s.App().StakersKeeper.GetEffectiveValidatorStakes(s.Ctx(), 0) - fmt.Println(effectiveStakes) - - fmt.Println("wait 60 seconds") s.CommitAfterSeconds(60) - bundleProposal, _ = s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) - fmt.Println(bundleProposal) - - // TODO: fails here s.RunTxBundlesSuccess(&bundletypes.MsgSubmitBundleProposal{ Creator: i.VALADDRESS_1_A, Staker: i.STAKER_1, @@ -339,11 +329,10 @@ var _ = Describe("zero delegation", Ordered, func() { StakeFraction: math.LegacyMustNewDecFromStr("1"), }) - s.RunTxBundlesSuccess(&bundletypes.MsgClaimUploaderRole{ - Creator: i.VALADDRESS_0_A, - Staker: i.STAKER_0, - PoolId: 0, - }) + // manually set next uploader + bundleProposal, _ := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) + bundleProposal.NextUploader = i.STAKER_0 + s.App().BundlesKeeper.SetBundleProposal(s.Ctx(), bundleProposal) s.CommitAfterSeconds(60) @@ -432,7 +421,7 @@ var _ = Describe("zero delegation", Ordered, func() { Expect(bundleProposal.PoolId).To(Equal(uint64(0))) Expect(bundleProposal.StorageId).To(Equal("P9edn0bjEfMU_lecFDIPLvGO2v2ltpFNUMWp5kgPddg")) Expect(bundleProposal.Uploader).To(Equal(i.STAKER_1)) - Expect(bundleProposal.NextUploader).To(Equal(i.STAKER_2)) + Expect(bundleProposal.NextUploader).To(Equal(i.STAKER_1)) Expect(bundleProposal.DataSize).To(Equal(uint64(100))) Expect(bundleProposal.DataHash).To(Equal("test_hash2")) Expect(bundleProposal.BundleSize).To(Equal(uint64(100))) @@ -518,11 +507,10 @@ var _ = Describe("zero delegation", Ordered, func() { StakeFraction: math.LegacyMustNewDecFromStr("1"), }) - s.RunTxBundlesSuccess(&bundletypes.MsgClaimUploaderRole{ - Creator: i.VALADDRESS_0_A, - Staker: i.STAKER_0, - PoolId: 0, - }) + // manually set next uploader + bundleProposal, _ := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) + bundleProposal.NextUploader = i.STAKER_0 + s.App().BundlesKeeper.SetBundleProposal(s.Ctx(), bundleProposal) s.CommitAfterSeconds(60) @@ -687,11 +675,10 @@ var _ = Describe("zero delegation", Ordered, func() { StakeFraction: math.LegacyMustNewDecFromStr("1"), }) - s.RunTxBundlesSuccess(&bundletypes.MsgClaimUploaderRole{ - Creator: i.VALADDRESS_0_A, - Staker: i.STAKER_0, - PoolId: 0, - }) + // manually set next uploader + bundleProposal, _ := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) + bundleProposal.NextUploader = i.STAKER_0 + s.App().BundlesKeeper.SetBundleProposal(s.Ctx(), bundleProposal) s.CommitAfterSeconds(60) @@ -794,15 +781,14 @@ var _ = Describe("zero delegation", Ordered, func() { StakeFraction: math.LegacyMustNewDecFromStr("1"), }) - s.RunTxBundlesSuccess(&bundletypes.MsgClaimUploaderRole{ - Creator: i.VALADDRESS_0_A, - Staker: i.STAKER_0, - PoolId: 0, - }) + // manually set next uploader + bundleProposal, _ := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) + bundleProposal.NextUploader = i.STAKER_0 + s.App().BundlesKeeper.SetBundleProposal(s.Ctx(), bundleProposal) s.CommitAfterSeconds(60) - s.RunTxBundlesSuccess(&bundletypes.MsgSubmitBundleProposal{ + s.RunTxBundlesError(&bundletypes.MsgSubmitBundleProposal{ Creator: i.VALADDRESS_0_A, Staker: i.STAKER_0, PoolId: 0, @@ -815,93 +801,5 @@ var _ = Describe("zero delegation", Ordered, func() { ToKey: "99", BundleSummary: "test_value", }) - - s.RunTxBundlesSuccess(&bundletypes.MsgVoteBundleProposal{ - Creator: i.VALADDRESS_1_A, - Staker: i.STAKER_1, - PoolId: 0, - StorageId: "y62A3tfbSNcNYDGoL-eXwzyV-Zc9Q0OVtDvR1biJmNI", - Vote: bundletypes.VOTE_TYPE_VALID, - }) - - initialBalanceStaker0 = s.GetBalanceFromAddress(i.STAKER_0) - initialBalanceValaddress0 = s.GetBalanceFromAddress(i.VALADDRESS_0_A) - - initialBalanceStaker1 = s.GetBalanceFromAddress(i.STAKER_1) - initialBalanceValaddress1 = s.GetBalanceFromAddress(i.VALADDRESS_1_A) - - // manually set next staker - bundleProposal, _ := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) - Expect(bundleProposal.NextUploader).To(BeEmpty()) - bundleProposal.NextUploader = i.STAKER_1 - s.App().BundlesKeeper.SetBundleProposal(s.Ctx(), bundleProposal) - - // ACT - s.CommitAfterSeconds(60) - s.CommitAfterSeconds(1) - - // ASSERT - // check if bundle got not finalized on pool - pool, poolFound := s.App().PoolKeeper.GetPool(s.Ctx(), 0) - Expect(poolFound).To(BeTrue()) - - Expect(pool.CurrentKey).To(Equal("")) - Expect(pool.CurrentSummary).To(BeEmpty()) - Expect(pool.CurrentIndex).To(BeZero()) - Expect(pool.TotalBundles).To(BeZero()) - - // check if finalized bundle exists - _, finalizedBundleFound := s.App().BundlesKeeper.GetFinalizedBundle(s.Ctx(), 0, 0) - Expect(finalizedBundleFound).To(BeFalse()) - - // check if bundle proposal got dropped - bundleProposal, bundleProposalFound := s.App().BundlesKeeper.GetBundleProposal(s.Ctx(), 0) - Expect(bundleProposalFound).To(BeTrue()) - - Expect(bundleProposal.PoolId).To(Equal(uint64(0))) - Expect(bundleProposal.StorageId).To(BeEmpty()) - Expect(bundleProposal.Uploader).To(BeEmpty()) - Expect(bundleProposal.NextUploader).To(BeEmpty()) - Expect(bundleProposal.DataSize).To(BeZero()) - Expect(bundleProposal.DataHash).To(BeEmpty()) - Expect(bundleProposal.BundleSize).To(BeZero()) - Expect(bundleProposal.FromKey).To(BeEmpty()) - Expect(bundleProposal.ToKey).To(BeEmpty()) - Expect(bundleProposal.BundleSummary).To(BeEmpty()) - Expect(bundleProposal.UpdatedAt).NotTo(BeZero()) - Expect(bundleProposal.VotersValid).To(BeEmpty()) - Expect(bundleProposal.VotersInvalid).To(BeEmpty()) - Expect(bundleProposal.VotersAbstain).To(BeEmpty()) - - // check uploader status - valaccountUploader, _ := s.App().StakersKeeper.GetValaccount(s.Ctx(), 0, i.STAKER_0) - Expect(valaccountUploader.Points).To(BeZero()) - - balanceValaddress := s.GetBalanceFromAddress(valaccountUploader.Valaddress) - Expect(balanceValaddress).To(Equal(initialBalanceValaddress0)) - - balanceUploader := s.GetBalanceFromAddress(valaccountUploader.Staker) - - Expect(balanceUploader).To(Equal(initialBalanceStaker0)) - Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(BeEmpty()) - - // check voter status - valaccountVoter, _ := s.App().StakersKeeper.GetValaccount(s.Ctx(), 0, i.STAKER_1) - Expect(valaccountVoter.Points).To(BeZero()) - - balanceVoterValaddress := s.GetBalanceFromAddress(valaccountVoter.Valaddress) - Expect(balanceVoterValaddress).To(Equal(initialBalanceValaddress1)) - - balanceVoter := s.GetBalanceFromAddress(valaccountVoter.Staker) - Expect(balanceVoter).To(Equal(initialBalanceStaker1)) - - Expect(balanceVoter).To(Equal(initialBalanceStaker1)) - Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_1, i.STAKER_1)).To(BeEmpty()) - - fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0) - - // assert total pool funds - Expect(s.App().FundersKeeper.GetTotalActiveFunding(s.Ctx(), fundingState.PoolId)[0].Amount.Uint64()).To(Equal(100 * i.KYVE)) - Expect(fundingState.ActiveFunderAddresses).To(HaveLen(1)) }) }) diff --git a/x/bundles/keeper/logic_bundles.go b/x/bundles/keeper/logic_bundles.go index 93cb5aec..9b318558 100644 --- a/x/bundles/keeper/logic_bundles.go +++ b/x/bundles/keeper/logic_bundles.go @@ -38,6 +38,9 @@ func (k Keeper) AssertPoolCanRun(ctx sdk.Context, poolId uint64) error { // Get the total and the highest delegation of a single validator in the pool totalDelegation, _ := k.stakerKeeper.GetTotalAndHighestDelegationOfPool(ctx, poolId) + if totalDelegation == 0 { + return types.ErrPoolHasZeroDelegation + } // Error if min delegation is not reached if totalDelegation < pool.MinDelegation { diff --git a/x/bundles/types/errors.go b/x/bundles/types/errors.go index 557386d6..64e00ba9 100644 --- a/x/bundles/types/errors.go +++ b/x/bundles/types/errors.go @@ -24,4 +24,5 @@ var ( ErrAlreadyVotedAbstain = errors.Register(ModuleName, 1206, "already voted abstain on bundle proposal") ErrVotingPowerTooHigh = errors.Register(ModuleName, 1207, "staker in pool has too much voting power") ErrEndKeyReached = errors.Register(ModuleName, 1208, "end key reached") + ErrPoolHasZeroDelegation = errors.Register(ModuleName, 1209, "pool has zero delegation") ) diff --git a/x/stakers/keeper/exported_functions.go b/x/stakers/keeper/exported_functions.go index 05d9d5cd..4f216fbd 100644 --- a/x/stakers/keeper/exported_functions.go +++ b/x/stakers/keeper/exported_functions.go @@ -229,7 +229,7 @@ func (k Keeper) GetEffectiveValidatorStakes(ctx sdk.Context, poolId uint64, must return validators[i].Stake > validators[j].Stake }) - if maxVotingPower.IsZero() { + if totalStake == 0 || maxVotingPower.IsZero() { return } @@ -240,20 +240,24 @@ func (k Keeper) GetEffectiveValidatorStakes(ctx sdk.Context, poolId uint64, must totalStakeRemainder -= int64(validator.Stake) effectiveStakes[validator.Address] -= uint64(amount) - for _, v := range validators[i+1:] { - effectiveStakes[v.Address] += uint64(math.LegacyNewDec(int64(effectiveStakes[v.Address])).QuoInt64(totalStakeRemainder).MulInt64(amount).TruncateInt64()) + if totalStakeRemainder > 0 { + for _, v := range validators[i+1:] { + effectiveStakes[v.Address] += uint64(math.LegacyNewDec(int64(effectiveStakes[v.Address])).QuoInt64(totalStakeRemainder).MulInt64(amount).TruncateInt64()) + } } } } - // TODO: take lowest validator with effective stake bigger than zero - lowestValidator := validators[len(validators)-1] scaleFactor := math.LegacyZeroDec() - if effectiveStakes[lowestValidator.Address] > 0 { - scaleFactor = math.LegacyNewDec(int64(lowestValidator.Stake)).QuoInt64(int64(effectiveStakes[lowestValidator.Address])) + // get lowest validator with effective stake still bigger than zero + for i := len(validators) - 1; i >= 0; i-- { + if effectiveStakes[validators[i].Address] > 0 { + scaleFactor = math.LegacyNewDec(int64(validators[i].Stake)).QuoInt64(int64(effectiveStakes[validators[i].Address])) + } } + // scale all effective stakes down to scale factor for _, validator := range validators { effectiveStakes[validator.Address] = uint64(scaleFactor.MulInt64(int64(effectiveStakes[validator.Address])).TruncateInt64()) } @@ -272,7 +276,11 @@ func (k Keeper) Slash(ctx sdk.Context, poolId uint64, staker string, slashType s // get real stake fraction from the effective stake which is truly at risk for getting slashed // by dividing it with the total bonded stake from the validator - stakeFraction := math.LegacyNewDec(int64(k.GetEffectiveValidatorStakes(ctx, poolId, staker)[staker])).QuoInt(validator.GetBondedTokens()) + stakeFraction := math.LegacyZeroDec() + + if !validator.GetBondedTokens().IsZero() { + stakeFraction = math.LegacyNewDec(int64(k.GetEffectiveValidatorStakes(ctx, poolId, staker)[staker])).QuoInt(validator.GetBondedTokens()) + } // the validator can only be slashed for his effective stake fraction in a pool, therefore we // update the slash fraction accordingly