From 29f8899e631a5b38dda63a9672be656fe0177923 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 01/13] distribution alternative allocation --- x/ccv/distribution/abci.go | 182 +++++++++++++++++++++++++++++++++++++ x/ccv/distribution/doc.go | 6 ++ 2 files changed, 188 insertions(+) create mode 100644 x/ccv/distribution/abci.go create mode 100644 x/ccv/distribution/doc.go diff --git a/x/ccv/distribution/abci.go b/x/ccv/distribution/abci.go new file mode 100644 index 0000000000..68c21d288e --- /dev/null +++ b/x/ccv/distribution/abci.go @@ -0,0 +1,182 @@ +package distribution + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. +type AppModuleBasic struct { + distribution.AppModuleBasic +} + +// AppModule embeds the Cosmos SDK's x/staking AppModule where we only override +// specific methods. +type AppModule struct { + // embed the Cosmos SDK's x/staking AppModule + distribution.AppModule + + //keeper keeper.Keeper + //accKeeper types.AccountKeeper + //bankKeeper types.BankKeeper +} + +// BeginBlocker sets the proposer for determining distribution during endblock +// and distribute rewards for the previous block +func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + // determine the total power signing the block + var previousTotalPower, sumPreviousPrecommitPower int64 + for _, voteInfo := range req.LastCommitInfo.GetVotes() { + previousTotalPower += voteInfo.Validator.Power + if voteInfo.SignedLastBlock { + sumPreviousPrecommitPower += voteInfo.Validator.Power + } + } + + // TODO this is Tendermint-dependent + // ref https://github.com/cosmos/cosmos-sdk/issues/3095 + if ctx.BlockHeight() > 1 { + previousProposer := k.GetPreviousProposerConsAddr(ctx) + k.AllocateTokens(ctx, sumPreviousPrecommitPower, previousTotalPower, previousProposer, req.LastCommitInfo.GetVotes()) + } + + // record the proposer for when we payout on the next block + consAddr := sdk.ConsAddress(req.Header.ProposerAddress) + k.SetPreviousProposerConsAddr(ctx, consAddr) +} + +// AllocateTokens handles distribution of the collected fees +// bondedVotes is a list of (validator address, validator voted on last block flag) for all +// validators in the bonded set. +func (k Keeper) AllocateTokens( + ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64, + previousProposer sdk.ConsAddress, bondedVotes []abci.VoteInfo, +) { + + logger := k.Logger(ctx) + + // fetch and clear the collected fees for distribution, since this is + // called in BeginBlock, collected fees will be from the previous block + // (and distributed to the previous proposer) + feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName) + feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) + feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) + + // transfer collected fees to the distribution module account + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt) + if err != nil { + panic(err) + } + + // temporary workaround to keep CanWithdrawInvariant happy + // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 + feePool := k.GetFeePool(ctx) + if totalPreviousPower == 0 { + feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) + k.SetFeePool(ctx, feePool) + return + } + + //// calculate fraction votes + //previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower)) + + //// calculate previous proposer reward + //baseProposerReward := k.GetBaseProposerReward(ctx) + //bonusProposerReward := k.GetBonusProposerReward(ctx) + //proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes)) + //proposerReward := feesCollected.MulDecTruncate(proposerMultiplier) + + //// pay previous proposer + //remaining := feesCollected + //proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer) + + //if proposerValidator != nil { + // ctx.EventManager().EmitEvent( + // sdk.NewEvent( + // types.EventTypeProposerReward, + // sdk.NewAttribute(sdk.AttributeKeyAmount, proposerReward.String()), + // sdk.NewAttribute(types.AttributeKeyValidator, proposerValidator.GetOperator().String()), + // ), + // ) + + // k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward) + // remaining = remaining.Sub(proposerReward) + //} else { + // // previous proposer can be unknown if say, the unbonding period is 1 block, so + // // e.g. a validator undelegates at block X, it's removed entirely by + // // block X+1's endblock, then X+2 we need to refer to the previous + // // proposer for X+1, but we've forgotten about them. + // logger.Error(fmt.Sprintf( + // "WARNING: Attempt to allocate proposer rewards to unknown proposer %s. "+ + // "This should happen only if the proposer unbonded completely within a single block, "+ + // "which generally should not happen except in exceptional circumstances (or fuzz testing). "+ + // "We recommend you investigate immediately.", + // previousProposer.String())) + //} + + // calculate fraction allocated to validators + remaining := feesCollected + communityTax := k.GetCommunityTax(ctx) + voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax) + + // allocate tokens proportionally to voting power + // TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376 + for _, vote := range bondedVotes { + validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address) + + // TODO consider microslashing for missing votes. + // ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701 + powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower)) + reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction) + k.AllocateTokensToValidator(ctx, validator, reward) + remaining = remaining.Sub(reward) + } + + // allocate community funding + feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) + k.SetFeePool(ctx, feePool) +} + +// AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commission +func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) { + // split tokens between validator and delegators according to commission + commission := tokens.MulDec(val.GetCommission()) + shared := tokens.Sub(commission) + + // update current commission + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeCommission, + sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()), + sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), + ), + ) + currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) + currentCommission.Commission = currentCommission.Commission.Add(commission...) + k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission) + + // update current rewards + currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator()) + currentRewards.Rewards = currentRewards.Rewards.Add(shared...) + k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards) + + // update outstanding rewards + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeRewards, + sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()), + sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), + ), + ) + outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) + outstanding.Rewards = outstanding.Rewards.Add(tokens...) + k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding) +} diff --git a/x/ccv/distribution/doc.go b/x/ccv/distribution/doc.go new file mode 100644 index 0000000000..8b9966c059 --- /dev/null +++ b/x/ccv/distribution/doc.go @@ -0,0 +1,6 @@ +/* +Package distribution implements a Cosmos SDK module, that provides an implementation +of the F1 fee distribution algorithm. It handles reward tracking, allocation, and +distribution. Please refer to the specification under /spec for further information. +*/ +package distribution From 1ef061a3eb52e398943d6989c174470906aed55a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 02/13] update distribution to work off of bonded validators not votes --- x/ccv/distribution/abci.go | 182 ----------------------------------- x/ccv/distribution/doc.go | 3 + x/ccv/distribution/module.go | 124 ++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 182 deletions(-) delete mode 100644 x/ccv/distribution/abci.go create mode 100644 x/ccv/distribution/module.go diff --git a/x/ccv/distribution/abci.go b/x/ccv/distribution/abci.go deleted file mode 100644 index 68c21d288e..0000000000 --- a/x/ccv/distribution/abci.go +++ /dev/null @@ -1,182 +0,0 @@ -package distribution - -import ( - "time" - - "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - abci "github.com/tendermint/tendermint/abci/types" -) - -// AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. -type AppModuleBasic struct { - distribution.AppModuleBasic -} - -// AppModule embeds the Cosmos SDK's x/staking AppModule where we only override -// specific methods. -type AppModule struct { - // embed the Cosmos SDK's x/staking AppModule - distribution.AppModule - - //keeper keeper.Keeper - //accKeeper types.AccountKeeper - //bankKeeper types.BankKeeper -} - -// BeginBlocker sets the proposer for determining distribution during endblock -// and distribute rewards for the previous block -func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { - defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - - // determine the total power signing the block - var previousTotalPower, sumPreviousPrecommitPower int64 - for _, voteInfo := range req.LastCommitInfo.GetVotes() { - previousTotalPower += voteInfo.Validator.Power - if voteInfo.SignedLastBlock { - sumPreviousPrecommitPower += voteInfo.Validator.Power - } - } - - // TODO this is Tendermint-dependent - // ref https://github.com/cosmos/cosmos-sdk/issues/3095 - if ctx.BlockHeight() > 1 { - previousProposer := k.GetPreviousProposerConsAddr(ctx) - k.AllocateTokens(ctx, sumPreviousPrecommitPower, previousTotalPower, previousProposer, req.LastCommitInfo.GetVotes()) - } - - // record the proposer for when we payout on the next block - consAddr := sdk.ConsAddress(req.Header.ProposerAddress) - k.SetPreviousProposerConsAddr(ctx, consAddr) -} - -// AllocateTokens handles distribution of the collected fees -// bondedVotes is a list of (validator address, validator voted on last block flag) for all -// validators in the bonded set. -func (k Keeper) AllocateTokens( - ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64, - previousProposer sdk.ConsAddress, bondedVotes []abci.VoteInfo, -) { - - logger := k.Logger(ctx) - - // fetch and clear the collected fees for distribution, since this is - // called in BeginBlock, collected fees will be from the previous block - // (and distributed to the previous proposer) - feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName) - feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) - feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) - - // transfer collected fees to the distribution module account - err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt) - if err != nil { - panic(err) - } - - // temporary workaround to keep CanWithdrawInvariant happy - // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 - feePool := k.GetFeePool(ctx) - if totalPreviousPower == 0 { - feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) - k.SetFeePool(ctx, feePool) - return - } - - //// calculate fraction votes - //previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower)) - - //// calculate previous proposer reward - //baseProposerReward := k.GetBaseProposerReward(ctx) - //bonusProposerReward := k.GetBonusProposerReward(ctx) - //proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes)) - //proposerReward := feesCollected.MulDecTruncate(proposerMultiplier) - - //// pay previous proposer - //remaining := feesCollected - //proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer) - - //if proposerValidator != nil { - // ctx.EventManager().EmitEvent( - // sdk.NewEvent( - // types.EventTypeProposerReward, - // sdk.NewAttribute(sdk.AttributeKeyAmount, proposerReward.String()), - // sdk.NewAttribute(types.AttributeKeyValidator, proposerValidator.GetOperator().String()), - // ), - // ) - - // k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward) - // remaining = remaining.Sub(proposerReward) - //} else { - // // previous proposer can be unknown if say, the unbonding period is 1 block, so - // // e.g. a validator undelegates at block X, it's removed entirely by - // // block X+1's endblock, then X+2 we need to refer to the previous - // // proposer for X+1, but we've forgotten about them. - // logger.Error(fmt.Sprintf( - // "WARNING: Attempt to allocate proposer rewards to unknown proposer %s. "+ - // "This should happen only if the proposer unbonded completely within a single block, "+ - // "which generally should not happen except in exceptional circumstances (or fuzz testing). "+ - // "We recommend you investigate immediately.", - // previousProposer.String())) - //} - - // calculate fraction allocated to validators - remaining := feesCollected - communityTax := k.GetCommunityTax(ctx) - voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax) - - // allocate tokens proportionally to voting power - // TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376 - for _, vote := range bondedVotes { - validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address) - - // TODO consider microslashing for missing votes. - // ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701 - powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower)) - reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction) - k.AllocateTokensToValidator(ctx, validator, reward) - remaining = remaining.Sub(reward) - } - - // allocate community funding - feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) - k.SetFeePool(ctx, feePool) -} - -// AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commission -func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) { - // split tokens between validator and delegators according to commission - commission := tokens.MulDec(val.GetCommission()) - shared := tokens.Sub(commission) - - // update current commission - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeCommission, - sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()), - sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), - ), - ) - currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) - currentCommission.Commission = currentCommission.Commission.Add(commission...) - k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission) - - // update current rewards - currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator()) - currentRewards.Rewards = currentRewards.Rewards.Add(shared...) - k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards) - - // update outstanding rewards - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeRewards, - sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()), - sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), - ), - ) - outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) - outstanding.Rewards = outstanding.Rewards.Add(tokens...) - k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding) -} diff --git a/x/ccv/distribution/doc.go b/x/ccv/distribution/doc.go index 8b9966c059..fdf2cbd284 100644 --- a/x/ccv/distribution/doc.go +++ b/x/ccv/distribution/doc.go @@ -2,5 +2,8 @@ Package distribution implements a Cosmos SDK module, that provides an implementation of the F1 fee distribution algorithm. It handles reward tracking, allocation, and distribution. Please refer to the specification under /spec for further information. + +This wrapped version of distribution excludes any proposer reward and is +intended to be used on a consumer chain. */ package distribution diff --git a/x/ccv/distribution/module.go b/x/ccv/distribution/module.go new file mode 100644 index 0000000000..f0a0371e02 --- /dev/null +++ b/x/ccv/distribution/module.go @@ -0,0 +1,124 @@ +package distribution + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. +type AppModuleBasic struct { + distr.AppModuleBasic +} + +// AppModule embeds the Cosmos SDK's x/distribution AppModule +type AppModule struct { + // embed the Cosmos SDK's x/distribution AppModule + distr.AppModule + + keeper keeper.Keeper + accountKeeper distrtypes.AccountKeeper + bankKeeper distrtypes.BankKeeper + stakingKeeper stakingkeeper.Keeper + + feeCollectorName string +} + +// NewAppModule creates a new AppModule object using the native x/distribution module +// AppModule constructor. +func NewAppModule( + cdc codec.Codec, keeper keeper.Keeper, ak distrtypes.AccountKeeper, + bk distrtypes.BankKeeper, sk stakingkeeper.Keeper, feeCollectorName string, +) AppModule { + distrAppMod := distr.NewAppModule(cdc, keeper, ak, bk, sk) + return AppModule{ + AppModule: distrAppMod, + keeper: keeper, + accountKeeper: ak, + bankKeeper: bk, + stakingKeeper: sk, + feeCollectorName: feeCollectorName, + } +} + +// BeginBlocker mirror functionality of cosmos-sdk/distribution BeginBlocker +// however it allocates no proposer reward +func (am AppModule) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) { + defer telemetry.ModuleMeasureSince(distrtypes.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + // TODO this is Tendermint-dependent + // ref https://github.com/cosmos/cosmos-sdk/issues/3095 + if ctx.BlockHeight() > 1 { + am.AllocateTokens(ctx) + } + + // record the proposer for when we payout on the next block + consAddr := sdk.ConsAddress(req.Header.ProposerAddress) + am.keeper.SetPreviousProposerConsAddr(ctx, consAddr) +} + +// AllocateTokens handles distribution of the collected fees +// bondedVotes is a list of (validator address, validator voted on last block flag) for all +// validators in the bonded set. +func (am AppModule) AllocateTokens( + ctx sdk.Context, +) { + + // fetch and clear the collected fees for distribution, since this is + // called in BeginBlock, collected fees will be from the previous block + // (and distributed to the previous proposer) + feeCollector := am.accountKeeper.GetModuleAccount(ctx, am.feeCollectorName) + feesCollectedInt := am.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) + feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) + + // transfer collected fees to the distribution module account + err := am.bankKeeper.SendCoinsFromModuleToModule(ctx, am.feeCollectorName, distrtypes.ModuleName, feesCollectedInt) + if err != nil { + panic(err) + } + + // temporary workaround to keep CanWithdrawInvariant happy + // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 + feePool := am.keeper.GetFeePool(ctx) + vs := am.stakingKeeper.GetValidatorSet() + totalPower := vs.TotalBondedTokens(ctx) + if totalPower.IsZero() { + feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) + am.keeper.SetFeePool(ctx, feePool) + return + } + + // calculate fraction allocated to validators + remaining := feesCollected + communityTax := am.keeper.GetCommunityTax(ctx) + voteMultiplier := sdk.OneDec().Sub(communityTax) + + // allocate tokens proportionally to voting power + // TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376 + + vs.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool { + + // TODO consider microslashing for missing votes. + // ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701 + powerFraction := sdk.NewDecFromInt(validator.GetTokens()).QuoTruncate(sdk.NewDecFromInt(totalPower)) + reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction) + am.keeper.AllocateTokensToValidator(ctx, validator, reward) + remaining = remaining.Sub(reward) + + return false + }) + + // allocate community funding + feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) + am.keeper.SetFeePool(ctx, feePool) +} From 8d7a017421d9033cb5866e8c61bb93d9e9390ff1 Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 03/13] copy of consumer app --- .../ante/msg_filter_ante.go | 58 ++ .../ante/msg_filter_ante_test.go | 85 ++ app/consumer-democracy/ante_handler.go | 58 ++ app/consumer-democracy/app.go | 746 ++++++++++++++++++ app/consumer-democracy/export.go | 198 +++++ app/consumer-democracy/genesis.go | 21 + 6 files changed, 1166 insertions(+) create mode 100644 app/consumer-democracy/ante/msg_filter_ante.go create mode 100644 app/consumer-democracy/ante/msg_filter_ante_test.go create mode 100644 app/consumer-democracy/ante_handler.go create mode 100644 app/consumer-democracy/app.go create mode 100644 app/consumer-democracy/export.go create mode 100644 app/consumer-democracy/genesis.go diff --git a/app/consumer-democracy/ante/msg_filter_ante.go b/app/consumer-democracy/ante/msg_filter_ante.go new file mode 100644 index 0000000000..5788f12a5e --- /dev/null +++ b/app/consumer-democracy/ante/msg_filter_ante.go @@ -0,0 +1,58 @@ +package ante + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var validMsgsCCVDisabled = map[string]struct{}{} + +type ( + // ConsumerKeeper defines the interface required by a consumer module keeper. + ConsumerKeeper interface { + GetProviderChannel(ctx sdk.Context) (string, bool) + } + + // MsgFilterDecorator defines an AnteHandler decorator that enables message + // filtering based on certain criteria. + MsgFilterDecorator struct { + ConsumerKeeper ConsumerKeeper + } +) + +func NewMsgFilterDecorator(k ConsumerKeeper) MsgFilterDecorator { + return MsgFilterDecorator{ + ConsumerKeeper: k, + } +} + +func (mfd MsgFilterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + currHeight := ctx.BlockHeight() + + // If the CCV channel has not yet been established, then we must only allow certain + // message types. + if _, ok := mfd.ConsumerKeeper.GetProviderChannel(ctx); !ok { + if !hasValidMsgsPreCCV(tx.GetMsgs()) { + return ctx, fmt.Errorf("tx contains unsupported message types at height %d", currHeight) + } + } + + return next(ctx, tx, simulate) +} + +func hasValidMsgsPreCCV(msgs []sdk.Msg) bool { + for _, msg := range msgs { + msgType := sdk.MsgTypeURL(msg) + + // Only accept IBC messages prior to the CCV channel being established. + // Note, rather than listing out all possible IBC message types, we assume + // all IBC message types have a correct and canonical prefix -- /ibc.* + if !strings.HasPrefix(msgType, "/ibc.") { + return false + } + } + + return true +} diff --git a/app/consumer-democracy/ante/msg_filter_ante_test.go b/app/consumer-democracy/ante/msg_filter_ante_test.go new file mode 100644 index 0000000000..6fb1f08955 --- /dev/null +++ b/app/consumer-democracy/ante/msg_filter_ante_test.go @@ -0,0 +1,85 @@ +package ante_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + appconsumer "github.com/cosmos/interchain-security/app/consumer" + "github.com/cosmos/interchain-security/app/consumer/ante" + "github.com/stretchr/testify/require" + "github.com/tendermint/spm/cosmoscmd" +) + +type consumerKeeper struct { + channelExists bool +} + +func (k consumerKeeper) GetProviderChannel(_ sdk.Context) (string, bool) { + return "", k.channelExists +} + +func noOpAnteDecorator() sdk.AnteHandler { + return func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + return ctx, nil + } +} + +func TestMsgFilterDecorator(t *testing.T) { + txCfg := cosmoscmd.MakeEncodingConfig(appconsumer.ModuleBasics).TxConfig + + testCases := []struct { + name string + ctx sdk.Context + consumerKeeper ante.ConsumerKeeper + msgs []sdk.Msg + expectErr bool + }{ + { + name: "valid tx pre-CCV", + ctx: sdk.Context{}, + consumerKeeper: consumerKeeper{channelExists: false}, + msgs: []sdk.Msg{ + &ibcclienttypes.MsgUpdateClient{}, + }, + expectErr: false, + }, + { + name: "invalid tx pre-CCV", + ctx: sdk.Context{}, + consumerKeeper: consumerKeeper{channelExists: false}, + msgs: []sdk.Msg{ + &banktypes.MsgSend{}, + }, + expectErr: true, + }, + { + name: "valid tx post-CCV", + ctx: sdk.Context{}, + consumerKeeper: consumerKeeper{channelExists: true}, + msgs: []sdk.Msg{ + &banktypes.MsgSend{}, + }, + expectErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + handler := ante.NewMsgFilterDecorator(tc.consumerKeeper) + + txBuilder := txCfg.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(tc.msgs...)) + + _, err := handler.AnteHandle(tc.ctx, txBuilder.GetTx(), false, noOpAnteDecorator()) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go new file mode 100644 index 0000000000..ea0984b41b --- /dev/null +++ b/app/consumer-democracy/ante_handler.go @@ -0,0 +1,58 @@ +package app + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" + consumerante "github.com/cosmos/interchain-security/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" +) + +// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC +// channel keeper. +type HandlerOptions struct { + ante.HandlerOptions + + IBCKeeper *ibckeeper.Keeper + ConsumerKeeper ibcconsumerkeeper.Keeper +} + +func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { + if options.AccountKeeper == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") + } + if options.BankKeeper == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") + } + if options.SignModeHandler == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") + } + + var sigGasConsumer = options.SigGasConsumer + if sigGasConsumer == nil { + sigGasConsumer = ante.DefaultSigVerificationGasConsumer + } + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ante.NewRejectExtensionOptionsDecorator(), + consumerante.NewMsgFilterDecorator(options.ConsumerKeeper), + ante.NewMempoolFeeDecorator(), + ante.NewValidateBasicDecorator(), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), + // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewSetPubKeyDecorator(options.AccountKeeper), + ante.NewValidateSigCountDecorator(options.AccountKeeper), + ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer), + ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.AccountKeeper), + ibcante.NewAnteDecorator(options.IBCKeeper), + } + + return sdk.ChainAnteDecorators(anteDecorators...), nil +} diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go new file mode 100644 index 0000000000..7f60c0dc47 --- /dev/null +++ b/app/consumer-democracy/app.go @@ -0,0 +1,746 @@ +package app + +import ( + "fmt" + "io" + stdlog "log" + "net/http" + "os" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/simapp" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/params" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/upgrade" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/cosmos/ibc-go/v3/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v3/modules/core" + ibcconnectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" + ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/spf13/cast" + "github.com/tendermint/spm/cosmoscmd" + abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" + "github.com/tendermint/tendermint/libs/log" + tmos "github.com/tendermint/tendermint/libs/os" + dbm "github.com/tendermint/tm-db" + + ibcconsumer "github.com/cosmos/interchain-security/x/ccv/consumer" + ibcconsumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" + ibcconsumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" + + // unnamed import of statik for swagger UI support + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" + ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" +) + +const ( + AppName = "interchain-security-c" + upgradeName = "v07-Theta" + AccountAddressPrefix = "cosmos" +) + +var ( + // DefaultNodeHome default home directories for the application daemon + DefaultNodeHome string + + // ModuleBasics defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, + params.AppModuleBasic{}, + crisis.AppModuleBasic{}, + slashing.AppModuleBasic{}, + feegrantmodule.AppModuleBasic{}, + authzmodule.AppModuleBasic{}, + ibc.AppModuleBasic{}, + upgrade.AppModuleBasic{}, + evidence.AppModuleBasic{}, + transfer.AppModuleBasic{}, + vesting.AppModuleBasic{}, + //router.AppModuleBasic{}, + ibcconsumer.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + ibcconsumertypes.ConsumerRedistributeName: nil, + ibcconsumertypes.ConsumerToSendToProviderName: nil, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + } +) + +var ( + _ simapp.App = (*App)(nil) + _ servertypes.Application = (*App)(nil) + _ cosmoscmd.CosmosApp = (*App)(nil) + _ ibctesting.TestingApp = (*App)(nil) +) + +// App extends an ABCI application, but with most of its parameters exported. +// They are exported for convenience in creating helper functions, as object +// capabilities aren't needed for testing. +type App struct { // nolint: golint + *baseapp.BaseApp + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + interfaceRegistry types.InterfaceRegistry + + invCheckPeriod uint + + // keys to access the substores + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + memKeys map[string]*sdk.MemoryStoreKey + + // keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + + // NOTE the distribution keeper should either be removed + // from consumer chain or set to use an independant + // different fee-pool from the consumer chain ConsumerKeeper + + CrisisKeeper crisiskeeper.Keeper + UpgradeKeeper upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + EvidenceKeeper evidencekeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + AuthzKeeper authzkeeper.Keeper + ConsumerKeeper ibcconsumerkeeper.Keeper + + // make scoped keepers public for test purposes + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + ScopedIBCConsumerKeeper capabilitykeeper.ScopedKeeper + + // the module manager + MM *module.Manager + + // simulation manager + sm *module.SimulationManager + configurator module.Configurator +} + +func init() { + userHomeDir, err := os.UserHomeDir() + if err != nil { + stdlog.Println("Failed to get home dir %2", err) + } + + DefaultNodeHome = filepath.Join(userHomeDir, "."+AppName) +} + +// New returns a reference to an initialized App. +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + encodingConfig cosmoscmd.EncodingConfig, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) cosmoscmd.App { + + appCodec := encodingConfig.Marshaler + legacyAmino := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + + bApp := baseapp.NewBaseApp(AppName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + + keys := sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, slashingtypes.StoreKey, + paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, + evidencetypes.StoreKey, ibctransfertypes.StoreKey, + capabilitytypes.StoreKey, feegrant.StoreKey, authzkeeper.StoreKey, + ibcconsumertypes.StoreKey, + ) + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) + + app := &App{ + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + interfaceRegistry: interfaceRegistry, + invCheckPeriod: invCheckPeriod, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, + } + + app.ParamsKeeper = initParamsKeeper( + appCodec, + legacyAmino, + keys[paramstypes.StoreKey], + tkeys[paramstypes.TStoreKey], + ) + + // set the BaseApp's parameter store + bApp.SetParamStore( + app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable( + paramskeeper.ConsensusParamsKeyTable()), + ) + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper( + appCodec, + keys[capabilitytypes.StoreKey], + memKeys[capabilitytypes.MemStoreKey], + ) + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) + scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + scopedIBCConsumerKeeper := app.CapabilityKeeper.ScopeToModule(ibcconsumertypes.ModuleName) + app.CapabilityKeeper.Seal() + + // add keepers + app.AccountKeeper = authkeeper.NewAccountKeeper( + appCodec, + keys[authtypes.StoreKey], + app.GetSubspace(authtypes.ModuleName), + authtypes.ProtoBaseAccount, + maccPerms, + ) + + // Remove the fee-pool from the group of blocked recipient addresses in bank + // this is required for the provider chain to be able to receive tokens from + // the consumer chain + bankBlockedAddrs := app.ModuleAccountAddrs() + delete(bankBlockedAddrs, authtypes.NewModuleAddress( + authtypes.FeeCollectorName).String()) + + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + app.GetSubspace(banktypes.ModuleName), + bankBlockedAddrs, + ) + app.AuthzKeeper = authzkeeper.NewKeeper( + keys[authzkeeper.StoreKey], + appCodec, + app.BaseApp.MsgServiceRouter(), + ) + app.FeeGrantKeeper = feegrantkeeper.NewKeeper( + appCodec, + keys[feegrant.StoreKey], + app.AccountKeeper, + ) + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + keys[slashingtypes.StoreKey], + &app.ConsumerKeeper, + app.GetSubspace(slashingtypes.ModuleName), + ) + app.CrisisKeeper = crisiskeeper.NewKeeper( + app.GetSubspace(crisistypes.ModuleName), + invCheckPeriod, + app.BankKeeper, + authtypes.FeeCollectorName, + ) + app.UpgradeKeeper = upgradekeeper.NewKeeper( + skipUpgradeHeights, + keys[upgradetypes.StoreKey], + appCodec, + homePath, + app.BaseApp, + ) + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, + keys[ibchost.StoreKey], + app.GetSubspace(ibchost.ModuleName), + &app.ConsumerKeeper, + app.UpgradeKeeper, + scopedIBCKeeper, + ) + + // Create CCV consumer and modules + app.ConsumerKeeper = ibcconsumerkeeper.NewKeeper( + appCodec, + keys[ibcconsumertypes.StoreKey], + app.GetSubspace(ibcconsumertypes.ModuleName), + scopedIBCConsumerKeeper, + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.IBCKeeper.ConnectionKeeper, + app.IBCKeeper.ClientKeeper, + app.SlashingKeeper, + app.BankKeeper, + app.AccountKeeper, + &app.TransferKeeper, + app.IBCKeeper, + authtypes.FeeCollectorName, + ) + + // consumer keeper satisfies the staking keeper interface + // of the slashing module + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + keys[slashingtypes.StoreKey], + &app.ConsumerKeeper, + app.GetSubspace(slashingtypes.ModuleName), + ) + + // register slashing module StakingHooks to the consumer keeper + app.ConsumerKeeper = *app.ConsumerKeeper.SetHooks(app.SlashingKeeper.Hooks()) + consumerModule := ibcconsumer.NewAppModule(app.ConsumerKeeper) + + app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, + keys[ibctransfertypes.StoreKey], + app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.AccountKeeper, + app.BankKeeper, + scopedTransferKeeper, + ) + transferModule := transfer.NewAppModule(app.TransferKeeper) + ibcmodule := transfer.NewIBCModule(app.TransferKeeper) + + // create static IBC router, add transfer route, then set and seal it + ibcRouter := porttypes.NewRouter() + ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcmodule) + ibcRouter.AddRoute(ibcconsumertypes.ModuleName, consumerModule) + app.IBCKeeper.SetRouter(ibcRouter) + + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, + keys[evidencetypes.StoreKey], + &app.ConsumerKeeper, + app.SlashingKeeper, + ) + + app.EvidenceKeeper = *evidenceKeeper + + skipGenesisInvariants := cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.MM = module.NewManager( + auth.NewAppModule(appCodec, app.AccountKeeper, nil), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.ConsumerKeeper), + upgrade.NewAppModule(app.UpgradeKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + ibc.NewAppModule(app.IBCKeeper), + params.NewAppModule(app.ParamsKeeper), + transferModule, + consumerModule, + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) + app.MM.SetOrderBeginBlockers( + // upgrades should be run first + upgradetypes.ModuleName, + capabilitytypes.ModuleName, + crisistypes.ModuleName, + ibctransfertypes.ModuleName, + ibchost.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + slashingtypes.ModuleName, + evidencetypes.ModuleName, + authz.ModuleName, + feegrant.ModuleName, + paramstypes.ModuleName, + vestingtypes.ModuleName, + ibcconsumertypes.ModuleName, + ) + app.MM.SetOrderEndBlockers( + crisistypes.ModuleName, + ibctransfertypes.ModuleName, + ibchost.ModuleName, + feegrant.ModuleName, + authz.ModuleName, + capabilitytypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + slashingtypes.ModuleName, + evidencetypes.ModuleName, + paramstypes.ModuleName, + upgradetypes.ModuleName, + vestingtypes.ModuleName, + ibcconsumertypes.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + app.MM.SetOrderInitGenesis( + capabilitytypes.ModuleName, + banktypes.ModuleName, + slashingtypes.ModuleName, + crisistypes.ModuleName, + ibchost.ModuleName, + evidencetypes.ModuleName, + ibctransfertypes.ModuleName, + feegrant.ModuleName, + authz.ModuleName, + authtypes.ModuleName, + + paramstypes.ModuleName, + upgradetypes.ModuleName, + vestingtypes.ModuleName, + ibcconsumertypes.ModuleName, + ) + + app.MM.RegisterInvariants(&app.CrisisKeeper) + app.MM.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) + + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + app.MM.RegisterServices(app.configurator) + + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + app.sm = module.NewSimulationManager( + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + params.NewAppModule(app.ParamsKeeper), + evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), + transferModule, + ) + + app.sm.RegisterStoreDecoders() + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + app.MountMemoryStores(memKeys) + + anteHandler, err := NewAnteHandler( + HandlerOptions{ + HandlerOptions: ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + IBCKeeper: app.IBCKeeper, + ConsumerKeeper: app.ConsumerKeeper, + }, + ) + if err != nil { + panic(fmt.Errorf("failed to create AnteHandler: %s", err)) + } + app.SetAnteHandler(anteHandler) + + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + + app.UpgradeKeeper.SetUpgradeHandler( + upgradeName, + func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + app.IBCKeeper.ConnectionKeeper.SetParams(ctx, ibcconnectiontypes.DefaultParams()) + + fromVM := make(map[string]uint64) + + for moduleName, eachModule := range app.MM.Modules { + fromVM[moduleName] = eachModule.ConsensusVersion() + } + + ctx.Logger().Info("start to run module migrations...") + + return app.MM.RunMigrations(ctx, app.configurator, fromVM) + }, + ) + + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(fmt.Sprintf("failed to read upgrade info from disk %s", err)) + } + + if upgradeInfo.Name == upgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := store.StoreUpgrades{} + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } + + if loadLatest { + if err := app.LoadLatestVersion(); err != nil { + tmos.Exit(fmt.Sprintf("failed to load latest version: %s", err)) + } + } + + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedTransferKeeper = scopedTransferKeeper + app.ScopedIBCConsumerKeeper = scopedIBCConsumerKeeper + + return app +} + +// Name returns the name of the App +func (app *App) Name() string { return app.BaseApp.Name() } + +// BeginBlocker application updates every begin block +func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.MM.BeginBlock(ctx, req) +} + +// EndBlocker application updates every end block +func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.MM.EndBlock(ctx, req) +} + +// InitChainer application update at chain initialization +func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + + app.UpgradeKeeper.SetModuleVersionMap(ctx, app.MM.GetVersionMap()) + return app.MM.InitGenesis(ctx, app.appCodec, genesisState) +} + +// LoadHeight loads a particular height +func (app *App) LoadHeight(height int64) error { + return app.LoadVersion(height) +} + +// ModuleAccountAddrs returns all the app's module account addresses. +func (app *App) ModuleAccountAddrs() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true + } + + return modAccAddrs +} + +// LegacyAmino returns App's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns the app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) AppCodec() codec.Codec { + return app.appCodec +} + +// InterfaceRegistry returns the InterfaceRegistry +func (app *App) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry +} + +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *App) GetKey(storeKey string) *sdk.KVStoreKey { + return app.keys[storeKey] +} + +// GetTKey returns the TransientStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *App) GetTKey(storeKey string) *sdk.TransientStoreKey { + return app.tkeys[storeKey] +} + +// GetMemKey returns the MemStoreKey for the provided mem key. +// +// NOTE: This is solely used for testing purposes. +func (app *App) GetMemKey(storeKey string) *sdk.MemoryStoreKey { + return app.memKeys[storeKey] +} + +// GetSubspace returns a param subspace for a given module name. +// +// NOTE: This is solely to be used for testing purposes. +func (app *App) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace +} + +// SimulationManager implements the SimulationApp interface +func (app *App) SimulationManager() *module.SimulationManager { + return app.sm +} + +// TestingApp functions + +// GetBaseApp implements the TestingApp interface. +func (app *App) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *App) GetStakingKeeper() ibcclienttypes.StakingKeeper { + return app.ConsumerKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *App) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *App) GetTxConfig() client.TxConfig { + return cosmoscmd.MakeEncodingConfig(ModuleBasics).TxConfig +} + +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *App) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + rpc.RegisterRoutes(clientCtx, apiSvr.Router) + // Register legacy tx routes. + authrest.RegisterTxRoutes(clientCtx, apiSvr.Router) + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + // Register new tendermint queries routes from grpc-gateway. + tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register legacy and grpc-gateway routes for all modules. + ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router) + ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if apiConfig.Swagger { + RegisterSwaggerAPI(apiSvr.Router) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *App) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *App) RegisterTendermintService(clientCtx client.Context) { + tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +} + +// RegisterSwaggerAPI registers swagger route with API Server +func RegisterSwaggerAPI(rtr *mux.Router) { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + + staticServer := http.FileServer(statikFS) + rtr.PathPrefix("/swagger/").Handler(http.StripPrefix("/swagger/", staticServer)) +} + +// GetMaccPerms returns a copy of the module account permissions +func GetMaccPerms() map[string][]string { + dupMaccPerms := make(map[string][]string) + for k, v := range maccPerms { + dupMaccPerms[k] = v + } + return dupMaccPerms +} + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + paramsKeeper.Subspace(authtypes.ModuleName) + paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(crisistypes.ModuleName) + paramsKeeper.Subspace(ibctransfertypes.ModuleName) + paramsKeeper.Subspace(ibchost.ModuleName) + paramsKeeper.Subspace(ibcconsumertypes.ModuleName) + + return paramsKeeper +} diff --git a/app/consumer-democracy/export.go b/app/consumer-democracy/export.go new file mode 100644 index 0000000000..c146ab7ad2 --- /dev/null +++ b/app/consumer-democracy/export.go @@ -0,0 +1,198 @@ +package app + +import ( + "encoding/json" + "fmt" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + tmtypes "github.com/tendermint/tendermint/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *App) ExportAppStateAndValidators( + forZeroHeight bool, jailAllowedAddrs []string, +) (servertypes.ExportedApp, error) { + + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) + } + + genState := app.MM.ExportGenesis(ctx, app.appCodec) + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := app.GetValidatorSet(ctx) + if err != nil { + return servertypes.ExportedApp{}, err + } + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, nil +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height +func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + // applyAllowedAddrs := false + + // check if there is a allowed address list + // if len(jailAllowedAddrs) > 0 { + // applyAllowedAddrs = true + // } + + // allowedAddrsMap := make(map[string]bool) + + // for _, addr := range jailAllowedAddrs { + // _, err := sdk.ValAddressFromBech32(addr) + // if err != nil { + // log.Fatal(err) + // } + // allowedAddrsMap[addr] = true + // } + + /* Just to be safe, assert the invariants on current state. */ + app.CrisisKeeper.AssertInvariants(ctx) + + /* Handle fee distribution state. */ + + // withdraw all validator commission + // app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + // _, err := app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + // if err != nil { + // panic(err) + // } + // return false + // }) + + // withdraw all delegator rewards + // dels := app.StakingKeeper.GetAllDelegations(ctx) + // for _, delegation := range dels { + // _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr()) + // if err != nil { + // panic(err) + // } + // } + + // clear validator slash events + // app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) + + // clear validator historical rewards + // app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + // app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + // // donate any unwithdrawn outstanding reward fraction tokens to the community pool + // scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) + // feePool := app.DistrKeeper.GetFeePool(ctx) + // feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) + // app.DistrKeeper.SetFeePool(ctx, feePool) + + // app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) + // return false + // }) + + // reinitialize all delegations + // for _, del := range dels { + // app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) + // app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) + // } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + // app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + // for i := range red.Entries { + // red.Entries[i].CreationHeight = 0 + // } + // app.StakingKeeper.SetRedelegation(ctx, red) + // return false + // }) + + // iterate through unbonding delegations, reset creation height + // app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + // for i := range ubd.Entries { + // ubd.Entries[i].CreationHeight = 0 + // } + // app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + // return false + // }) + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + // store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) + // iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + // counter := int16(0) + + // for ; iter.Valid(); iter.Next() { + // addr := sdk.ValAddress(iter.Key()[1:]) + // validator, found := app.StakingKeeper.GetValidator(ctx, addr) + // if !found { + // panic("expected validator, not found") + // } + + // validator.UnbondingHeight = 0 + // if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + // validator.Jailed = true + // } + + // app.StakingKeeper.SetValidator(ctx, validator) + // counter++ + // } + + // iter.Close() + + // if _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx); err != nil { + // panic(err) + // } + + /* Handle slashing state. */ + + // reset start height on signing infos + app.SlashingKeeper.IterateValidatorSigningInfos( + ctx, + func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + return false + }, + ) +} + +// GetValidatorSet returns a slice of bonded validators. +func (app *App) GetValidatorSet(ctx sdk.Context) ([]tmtypes.GenesisValidator, error) { + cVals := app.ConsumerKeeper.GetAllCCValidator(ctx) + if len(cVals) == 0 { + return nil, fmt.Errorf("empty validator set") + } + + vals := []tmtypes.GenesisValidator{} + for _, v := range cVals { + vals = append(vals, tmtypes.GenesisValidator{Address: v.Address, Power: v.Power}) + } + return vals, nil +} diff --git a/app/consumer-democracy/genesis.go b/app/consumer-democracy/genesis.go new file mode 100644 index 0000000000..5bf0c1da80 --- /dev/null +++ b/app/consumer-democracy/genesis.go @@ -0,0 +1,21 @@ +package app + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// The genesis state of the blockchain is represented here as a map of raw json +// messages key'd by a identifier string. +// The identifier is used to determine which module genesis information belongs +// to so it may be appropriately routed during init chain. +// Within this application default genesis information is retrieved from +// the ModuleBasicManager which populates json from each BasicModule +// object provided to it during init. +type GenesisState map[string]json.RawMessage + +// NewDefaultGenesisState generates the default state for the application. +func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { + return ModuleBasics.DefaultGenesis(cdc) +} From f6dc67afb4a5a713e08935b465d32d954adf875c Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 04/13] added ccvstaking, ccvdistribution, ccvgov and ccvminting --- app/consumer-democracy/app.go | 144 ++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 17 deletions(-) diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 7f60c0dc47..8068cbf3fb 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -50,8 +50,11 @@ import ( feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" @@ -77,6 +80,29 @@ import ( tmos "github.com/tendermint/tendermint/libs/os" dbm "github.com/tendermint/tm-db" + // add ccv distribution CALUM + distr "github.com/cosmos/cosmos-sdk/x/distribution" + ccvdistrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" + ccvdistrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + ccvdistrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + ccvdistr "github.com/cosmos/interchain-security/x/ccv/distribution" + + // add ccv staking BILY + ccvstakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + ccvstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ccvstaking "github.com/cosmos/interchain-security/x/ccv/staking" + + // add gov Billy + ccvgov "github.com/cosmos/cosmos-sdk/x/gov" + ccvgovkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + ccvgovtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + // add mint + ccvmint "github.com/cosmos/cosmos-sdk/x/mint" + ccvmintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + ccvminttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ibcconsumer "github.com/cosmos/interchain-security/x/ccv/consumer" ibcconsumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" ibcconsumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" @@ -87,7 +113,7 @@ import ( ) const ( - AppName = "interchain-security-c" + AppName = "interchain-security-cd" upgradeName = "v07-Theta" AccountAddressPrefix = "cosmos" ) @@ -103,6 +129,13 @@ var ( auth.AppModuleBasic{}, bank.AppModuleBasic{}, capability.AppModuleBasic{}, + ccvstaking.AppModuleBasic{}, + ccvdistr.AppModuleBasic{}, + ccvmint.AppModuleBasic{}, + ccvgov.NewAppModuleBasic( + // TODO: eventually remove upgrade proposal handler and cancel proposal handler + paramsclient.ProposalHandler, ccvdistrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, + ), params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, @@ -120,9 +153,14 @@ var ( // module account permissions maccPerms = map[string][]string{ authtypes.FeeCollectorName: nil, + ccvstakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + ccvstakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + ccvdistrtypes.ModuleName: nil, + ccvminttypes.ModuleName: {authtypes.Minter}, ibcconsumertypes.ConsumerRedistributeName: nil, ibcconsumertypes.ConsumerToSendToProviderName: nil, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ccvgovtypes.ModuleName: {authtypes.Burner}, } ) @@ -153,21 +191,20 @@ type App struct { // nolint: golint AccountKeeper authkeeper.AccountKeeper BankKeeper bankkeeper.Keeper CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper ccvstakingkeeper.Keeper SlashingKeeper slashingkeeper.Keeper - - // NOTE the distribution keeper should either be removed - // from consumer chain or set to use an independant - // different fee-pool from the consumer chain ConsumerKeeper - - CrisisKeeper crisiskeeper.Keeper - UpgradeKeeper upgradekeeper.Keeper - ParamsKeeper paramskeeper.Keeper - IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly - EvidenceKeeper evidencekeeper.Keeper - TransferKeeper ibctransferkeeper.Keeper - FeeGrantKeeper feegrantkeeper.Keeper - AuthzKeeper authzkeeper.Keeper - ConsumerKeeper ibcconsumerkeeper.Keeper + MintKeeper ccvmintkeeper.Keeper + DistrKeeper ccvdistrkeeper.Keeper + GovKeeper ccvgovkeeper.Keeper + CrisisKeeper crisiskeeper.Keeper + UpgradeKeeper upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + EvidenceKeeper evidencekeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + AuthzKeeper authzkeeper.Keeper + ConsumerKeeper ibcconsumerkeeper.Keeper // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -215,11 +252,12 @@ func New( bApp.SetInterfaceRegistry(interfaceRegistry) keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, slashingtypes.StoreKey, + authtypes.StoreKey, banktypes.StoreKey, ccvstakingtypes.StoreKey, slashingtypes.StoreKey, + ccvgovtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, feegrant.StoreKey, authzkeeper.StoreKey, - ibcconsumertypes.StoreKey, + ibcconsumertypes.StoreKey, ccvdistrtypes.StoreKey, ccvminttypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -292,12 +330,55 @@ func New( keys[feegrant.StoreKey], app.AccountKeeper, ) + + ccvstakingKeeper := ccvstakingkeeper.NewKeeper( + appCodec, + keys[ccvstakingtypes.StoreKey], + tkeys[ccvstakingtypes.TStoreKey], + app.AccountKeeper, + app.BankKeeper, + app.GetSubspace(ccvstakingtypes.ModuleName), + ) + + app.MintKeeper = ccvmintkeeper.NewKeeper( + appCodec, keys[ccvminttypes.StoreKey], app.GetSubspace(ccvminttypes.ModuleName), &ccvstakingKeeper, + app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, + ) + + // register the proposal types + ccvgovRouter := ccvgovtypes.NewRouter() + ccvgovRouter.AddRoute(ccvgovtypes.RouterKey, ccvgovtypes.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). + AddRoute(ccvdistrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). + // TODO: remove upgrade handler from gov once admin module or decision for only signaling proposal is made. + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) + govKeeper := ccvgovkeeper.NewKeeper( + appCodec, keys[ccvgovtypes.StoreKey], app.GetSubspace(ccvgovtypes.ModuleName), app.AccountKeeper, app.BankKeeper, + &ccvstakingKeeper, ccvgovRouter, + ) + + app.GovKeeper = *govKeeper.SetHooks( + ccvgovtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + app.SlashingKeeper = slashingkeeper.NewKeeper( appCodec, keys[slashingtypes.StoreKey], &app.ConsumerKeeper, app.GetSubspace(slashingtypes.ModuleName), ) + app.DistrKeeper = ccvdistrkeeper.NewKeeper( + appCodec, + keys[ccvdistrtypes.StoreKey], + app.GetSubspace(ccvdistrtypes.ModuleName), + app.AccountKeeper, + app.BankKeeper, + &ccvstakingKeeper, + authtypes.FeeCollectorName, + app.ModuleAccountAddrs(), + ) app.CrisisKeeper = crisiskeeper.NewKeeper( app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, @@ -311,6 +392,14 @@ func New( homePath, app.BaseApp, ) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + // NOTE: slashing hook was removed since it's only relevant for consumerKeeper + app.StakingKeeper = *ccvstakingKeeper.SetHooks( + ccvstakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks()), + ) + app.IBCKeeper = ibckeeper.NewKeeper( appCodec, keys[ibchost.StoreKey], @@ -392,8 +481,12 @@ func New( capability.NewAppModule(appCodec, *app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.ConsumerKeeper), + ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName), + ccvstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + ccvmint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), + ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ibc.NewAppModule(app.IBCKeeper), @@ -412,10 +505,13 @@ func New( upgradetypes.ModuleName, capabilitytypes.ModuleName, crisistypes.ModuleName, + ccvminttypes.ModuleName, + ccvstakingtypes.ModuleName, ibctransfertypes.ModuleName, ibchost.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + ccvgovtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, @@ -426,6 +522,8 @@ func New( ) app.MM.SetOrderEndBlockers( crisistypes.ModuleName, + ccvgovtypes.ModuleName, + ccvstakingtypes.ModuleName, ibctransfertypes.ModuleName, ibchost.ModuleName, feegrant.ModuleName, @@ -434,6 +532,7 @@ func New( authtypes.ModuleName, banktypes.ModuleName, slashingtypes.ModuleName, + ccvminttypes.ModuleName, evidencetypes.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, @@ -450,7 +549,10 @@ func New( app.MM.SetOrderInitGenesis( capabilitytypes.ModuleName, banktypes.ModuleName, + ccvstakingtypes.ModuleName, + ccvminttypes.ModuleName, slashingtypes.ModuleName, + ccvgovtypes.ModuleName, crisistypes.ModuleName, ibchost.ModuleName, evidencetypes.ModuleName, @@ -480,7 +582,11 @@ func New( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + ccvmint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), + ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName), + ccvstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), transferModule, @@ -736,7 +842,11 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(authtypes.ModuleName) paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(ccvstakingtypes.ModuleName) + paramsKeeper.Subspace(ccvminttypes.ModuleName) + paramsKeeper.Subspace(ccvdistrtypes.ModuleName) paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(ccvgovtypes.ModuleName).WithKeyTable(ccvgovtypes.ParamKeyTable()) paramsKeeper.Subspace(crisistypes.ModuleName) paramsKeeper.Subspace(ibctransfertypes.ModuleName) paramsKeeper.Subspace(ibchost.ModuleName) From 4598af5980dcee9da1d550e9845e99743b26cd99 Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 05/13] add cmd interchain-security-cdd --- Makefile | 5 +++-- cmd/interchain-security-cdd/main.go | 32 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 cmd/interchain-security-cdd/main.go diff --git a/Makefile b/Makefile index c5193aaf7b..ca82e6ff30 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,9 @@ install: go.sum export GOFLAGS='-buildmode=pie' export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2" export CGO_LDFLAGS="-Wl,-z,relro,-z,now -fstack-protector" - go install $(BUILD_FLAGS) ./cmd/interchain-security-pd - go install $(BUILD_FLAGS) ./cmd/interchain-security-cd + # go install $(BUILD_FLAGS) ./cmd/interchain-security-pd + # go install $(BUILD_FLAGS) ./cmd/interchain-security-cd + go install $(BUILD_FLAGS) ./cmd/interchain-security-cdd # run all tests: unit, e2e, diff, and integration test: diff --git a/cmd/interchain-security-cdd/main.go b/cmd/interchain-security-cdd/main.go new file mode 100644 index 0000000000..6b9bbffe60 --- /dev/null +++ b/cmd/interchain-security-cdd/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + app "github.com/cosmos/interchain-security/app/consumer-democracy" + "github.com/tendermint/spm/cosmoscmd" +) + +func main() { + rootCmd, _ := cosmoscmd.NewRootCmd( + app.AppName, + app.AccountAddressPrefix, + app.DefaultNodeHome, + app.AppName, + app.ModuleBasics, + app.New, + // this line is used by starport scaffolding # root/arguments + ) + + if err := svrcmd.Execute(rootCmd, app.DefaultNodeHome); err != nil { + switch e := err.(type) { + case server.ErrorCode: + os.Exit(e.Code) + + default: + os.Exit(1) + } + } +} From 2725db75a2c647dabe82fa3c301fcb700692545d Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 06/13] distribution tokens should be coming from ConsumerRedistributeName not feeCollector --- x/ccv/distribution/module.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/ccv/distribution/module.go b/x/ccv/distribution/module.go index f0a0371e02..6c384bb208 100644 --- a/x/ccv/distribution/module.go +++ b/x/ccv/distribution/module.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" abci "github.com/tendermint/tendermint/abci/types" @@ -77,12 +78,12 @@ func (am AppModule) AllocateTokens( // fetch and clear the collected fees for distribution, since this is // called in BeginBlock, collected fees will be from the previous block // (and distributed to the previous proposer) - feeCollector := am.accountKeeper.GetModuleAccount(ctx, am.feeCollectorName) + feeCollector := am.accountKeeper.GetModuleAccount(ctx, consumertypes.ConsumerRedistributeName) feesCollectedInt := am.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) // transfer collected fees to the distribution module account - err := am.bankKeeper.SendCoinsFromModuleToModule(ctx, am.feeCollectorName, distrtypes.ModuleName, feesCollectedInt) + err := am.bankKeeper.SendCoinsFromModuleToModule(ctx, consumertypes.ConsumerRedistributeName, distrtypes.ModuleName, feesCollectedInt) if err != nil { panic(err) } From fede05a00e8767c2cead12933a86369b01db3cad Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 07/13] beginning of tests --- app/consumer-democracy/app.go | 101 +++--- testutil/simapp/simapp.go | 22 ++ x/ccv/consumer/keeper/distribution.go | 3 + x/ccv/democracy/democracy_test.go | 345 +++++++++++++++++++ x/ccv/democracy/distribution/doc.go | 9 + x/ccv/{ => democracy}/distribution/module.go | 9 +- x/ccv/{ => democracy}/staking/doc.go | 2 +- x/ccv/{ => democracy}/staking/module.go | 0 x/ccv/distribution/doc.go | 9 - 9 files changed, 440 insertions(+), 60 deletions(-) create mode 100644 x/ccv/democracy/democracy_test.go create mode 100644 x/ccv/democracy/distribution/doc.go rename x/ccv/{ => democracy}/distribution/module.go (94%) rename x/ccv/{ => democracy}/staking/doc.go (85%) rename x/ccv/{ => democracy}/staking/module.go (100%) delete mode 100644 x/ccv/distribution/doc.go diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 8068cbf3fb..593534a6ab 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -85,12 +85,12 @@ import ( ccvdistrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" ccvdistrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" ccvdistrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - ccvdistr "github.com/cosmos/interchain-security/x/ccv/distribution" + ccvdistr "github.com/cosmos/interchain-security/x/ccv/democracy/distribution" // add ccv staking BILY ccvstakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ccvstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvstaking "github.com/cosmos/interchain-security/x/ccv/staking" + ccvstaking "github.com/cosmos/interchain-security/x/ccv/democracy/staking" // add gov Billy ccvgov "github.com/cosmos/cosmos-sdk/x/gov" @@ -130,8 +130,8 @@ var ( bank.AppModuleBasic{}, capability.AppModuleBasic{}, ccvstaking.AppModuleBasic{}, - ccvdistr.AppModuleBasic{}, ccvmint.AppModuleBasic{}, + ccvdistr.AppModuleBasic{}, ccvgov.NewAppModuleBasic( // TODO: eventually remove upgrade proposal handler and cancel proposal handler paramsclient.ProposalHandler, ccvdistrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, @@ -252,14 +252,14 @@ func New( bApp.SetInterfaceRegistry(interfaceRegistry) keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, ccvstakingtypes.StoreKey, slashingtypes.StoreKey, - ccvgovtypes.StoreKey, - paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, + authtypes.StoreKey, banktypes.StoreKey, ccvstakingtypes.StoreKey, + ccvminttypes.StoreKey, ccvdistrtypes.StoreKey, slashingtypes.StoreKey, + ccvgovtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, - capabilitytypes.StoreKey, feegrant.StoreKey, authzkeeper.StoreKey, - ibcconsumertypes.StoreKey, ccvdistrtypes.StoreKey, ccvminttypes.StoreKey, + capabilitytypes.StoreKey, authzkeeper.StoreKey, + ibcconsumertypes.StoreKey, ) - tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, ccvstakingtypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) app := &App{ @@ -345,24 +345,6 @@ func New( app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, ) - // register the proposal types - ccvgovRouter := ccvgovtypes.NewRouter() - ccvgovRouter.AddRoute(ccvgovtypes.RouterKey, ccvgovtypes.ProposalHandler). - AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). - AddRoute(ccvdistrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). - // TODO: remove upgrade handler from gov once admin module or decision for only signaling proposal is made. - AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) - govKeeper := ccvgovkeeper.NewKeeper( - appCodec, keys[ccvgovtypes.StoreKey], app.GetSubspace(ccvgovtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &ccvstakingKeeper, ccvgovRouter, - ) - - app.GovKeeper = *govKeeper.SetHooks( - ccvgovtypes.NewMultiGovHooks( - // register the governance hooks - ), - ) - app.SlashingKeeper = slashingkeeper.NewKeeper( appCodec, keys[slashingtypes.StoreKey], @@ -376,7 +358,7 @@ func New( app.AccountKeeper, app.BankKeeper, &ccvstakingKeeper, - authtypes.FeeCollectorName, + ibcconsumertypes.ConsumerRedistributeName, app.ModuleAccountAddrs(), ) app.CrisisKeeper = crisiskeeper.NewKeeper( @@ -400,6 +382,24 @@ func New( ccvstakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks()), ) + // register the proposal types + ccvgovRouter := ccvgovtypes.NewRouter() + ccvgovRouter.AddRoute(ccvgovtypes.RouterKey, ccvgovtypes.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). + AddRoute(ccvdistrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). + // TODO: remove upgrade handler from gov once admin module or decision for only signaling proposal is made. + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) + govKeeper := ccvgovkeeper.NewKeeper( + appCodec, keys[ccvgovtypes.StoreKey], app.GetSubspace(ccvgovtypes.ModuleName), app.AccountKeeper, app.BankKeeper, + &ccvstakingKeeper, ccvgovRouter, + ) + + app.GovKeeper = *govKeeper.SetHooks( + ccvgovtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + app.IBCKeeper = ibckeeper.NewKeeper( appCodec, keys[ibchost.StoreKey], @@ -480,17 +480,17 @@ func New( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + ccvmint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.ConsumerKeeper), ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName), ccvstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), - ccvmint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), - ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + params.NewAppModule(app.ParamsKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ibc.NewAppModule(app.IBCKeeper), - params.NewAppModule(app.ParamsKeeper), transferModule, consumerModule, ) @@ -504,39 +504,41 @@ func New( // upgrades should be run first upgradetypes.ModuleName, capabilitytypes.ModuleName, - crisistypes.ModuleName, ccvminttypes.ModuleName, + ccvdistrtypes.ModuleName, + slashingtypes.ModuleName, + evidencetypes.ModuleName, ccvstakingtypes.ModuleName, - ibctransfertypes.ModuleName, - ibchost.ModuleName, authtypes.ModuleName, banktypes.ModuleName, ccvgovtypes.ModuleName, - slashingtypes.ModuleName, - evidencetypes.ModuleName, + crisistypes.ModuleName, authz.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, vestingtypes.ModuleName, + ibctransfertypes.ModuleName, + ibchost.ModuleName, ibcconsumertypes.ModuleName, ) app.MM.SetOrderEndBlockers( crisistypes.ModuleName, ccvgovtypes.ModuleName, ccvstakingtypes.ModuleName, - ibctransfertypes.ModuleName, - ibchost.ModuleName, - feegrant.ModuleName, - authz.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + ccvdistrtypes.ModuleName, slashingtypes.ModuleName, ccvminttypes.ModuleName, evidencetypes.ModuleName, + authz.ModuleName, + feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, + ibctransfertypes.ModuleName, + ibchost.ModuleName, ibcconsumertypes.ModuleName, ) @@ -548,22 +550,22 @@ func New( // can do so safely. app.MM.SetOrderInitGenesis( capabilitytypes.ModuleName, + authtypes.ModuleName, banktypes.ModuleName, + ccvdistrtypes.ModuleName, ccvstakingtypes.ModuleName, - ccvminttypes.ModuleName, slashingtypes.ModuleName, ccvgovtypes.ModuleName, + ccvminttypes.ModuleName, crisistypes.ModuleName, - ibchost.ModuleName, evidencetypes.ModuleName, - ibctransfertypes.ModuleName, - feegrant.ModuleName, authz.ModuleName, - authtypes.ModuleName, - + feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, + ibchost.ModuleName, + ibctransfertypes.ModuleName, ibcconsumertypes.ModuleName, ) @@ -583,12 +585,13 @@ func New( capability.NewAppModule(appCodec, *app.CapabilityKeeper), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ccvmint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), - ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName), ccvstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, authtypes.FeeCollectorName), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), transferModule, ) diff --git a/testutil/simapp/simapp.go b/testutil/simapp/simapp.go index f18de89c9f..74e5c0bfd9 100644 --- a/testutil/simapp/simapp.go +++ b/testutil/simapp/simapp.go @@ -13,6 +13,7 @@ import ( tmdb "github.com/tendermint/tm-db" appConsumer "github.com/cosmos/interchain-security/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/app/consumer-democracy" appProvider "github.com/cosmos/interchain-security/app/provider" ) @@ -24,6 +25,14 @@ func SetupTestingappProvider() (ibctesting.TestingApp, map[string]json.RawMessag return testApp, appProvider.NewDefaultGenesisState(encoding.Marshaler) } +func SetupTestingAppConsumerDemocracy() (ibctesting.TestingApp, map[string]json.RawMessage) { + db := tmdb.NewMemDB() + // encCdc := app.MakeTestEncodingConfig() + encoding := cosmoscmd.MakeEncodingConfig(appConsumerDemocracy.ModuleBasics) + testApp := appConsumerDemocracy.New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encoding, simapp.EmptyAppOptions{}).(ibctesting.TestingApp) + return testApp, appConsumerDemocracy.NewDefaultGenesisState(encoding.Marshaler) +} + func SetupTestingAppConsumer() (ibctesting.TestingApp, map[string]json.RawMessage) { db := tmdb.NewMemDB() // encCdc := app.MakeTestEncodingConfig() @@ -55,3 +64,16 @@ func NewProviderConsumerCoordinator(t *testing.T) (*ibctesting.Coordinator, *ibc consumerChain := coordinator.GetChain(chainID) return coordinator, providerChain, consumerChain } + +// NewCoordinator initializes Coordinator with 0 TestChains +func NewProviderConsumerDemocracyCoordinator(t *testing.T) (*ibctesting.Coordinator, *ibctesting.TestChain, *ibctesting.TestChain) { + coordinator := NewBasicCoordinator(t) + chainID := ibctesting.GetChainID(1) + coordinator.Chains[chainID] = ibctesting.NewTestChain(t, coordinator, SetupTestingappProvider, chainID) + providerChain := coordinator.GetChain(chainID) + chainID = ibctesting.GetChainID(2) + coordinator.Chains[chainID] = ibctesting.NewTestChainWithValSet(t, coordinator, + SetupTestingAppConsumerDemocracy, chainID, providerChain.Vals, providerChain.Signers) + consumerChain := coordinator.GetChain(chainID) + return coordinator, providerChain, consumerChain +} diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index 1e17ed178d..befd832c08 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -1,6 +1,7 @@ package keeper import ( + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -41,9 +42,11 @@ func (k Keeper) DistributeToProviderValidatorSet(ctx sdk.Context) error { decFPTokens := sdk.NewDecCoinsFromCoins(fpTokens...) // NOTE the truncated decimal remainder will be sent to the provider fee pool consRedistrTokens, _ := decFPTokens.MulDec(frac).TruncateDecimal() + fmt.Println("consRedistrTokens", consRedistrTokens) err = k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ConsumerRedistributeName, consRedistrTokens) if err != nil { + fmt.Println("error sending to ConsumerRedistributeName", err) return err } diff --git a/x/ccv/democracy/democracy_test.go b/x/ccv/democracy/democracy_test.go new file mode 100644 index 0000000000..4085765286 --- /dev/null +++ b/x/ccv/democracy/democracy_test.go @@ -0,0 +1,345 @@ +package democracy_test + +import ( + "bytes" + "fmt" + "regexp" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + + appConsumer "github.com/cosmos/interchain-security/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/app/provider" + "github.com/cosmos/interchain-security/testutil/simapp" + consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" + providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" + "github.com/cosmos/interchain-security/x/ccv/types" + "github.com/cosmos/interchain-security/x/ccv/utils" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/stretchr/testify/suite" +) + +type ConsumerDemocracyTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains + providerChain *ibctesting.TestChain + consumerChain *ibctesting.TestChain + + path *ibctesting.Path + transferPath *ibctesting.Path + + // providerDistrIndex int +} + +func (s *ConsumerDemocracyTestSuite) SetupTest() { + s.coordinator, s.providerChain, s.consumerChain = simapp.NewProviderConsumerDemocracyCoordinator(s.T()) + + // valsets must match + providerValUpdates := tmtypes.TM2PB.ValidatorUpdates(s.providerChain.Vals) + consumerValUpdates := tmtypes.TM2PB.ValidatorUpdates(s.consumerChain.Vals) + s.Require().True(len(providerValUpdates) == len(consumerValUpdates), "initial valset not matching") + for i := 0; i < len(providerValUpdates); i++ { + addr1 := utils.GetChangePubKeyAddress(providerValUpdates[i]) + addr2 := utils.GetChangePubKeyAddress(consumerValUpdates[i]) + s.Require().True(bytes.Equal(addr1, addr2), "validator mismatch") + } + + // move both chains to the next block + s.providerChain.NextBlock() + s.consumerChain.NextBlock() + + // create consumer client on provider chain and set as consumer client for consumer chainID in provider keeper. + err := s.providerChain.App.(*appProvider.App).ProviderKeeper.CreateConsumerClient( + s.providerCtx(), + s.consumerChain.ChainID, + s.consumerChain.LastHeader.GetHeight().(clienttypes.Height), + false, + ) + s.Require().NoError(err) + + // move provider to next block to commit the state + s.providerChain.NextBlock() + + // initialize the consumer chain with the genesis state stored on the provider + consumerGenesis, found := s.providerChain.App.(*appProvider.App).ProviderKeeper.GetConsumerGenesis( + s.providerCtx(), + s.consumerChain.ChainID, + ) + s.Require().True(found, "consumer genesis not found") + s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.InitGenesis(s.consumerChain.GetContext(), &consumerGenesis) + + // create path for the CCV channel + s.path = ibctesting.NewPath(s.consumerChain, s.providerChain) + + // update CCV path with correct info + // - set provider endpoint's clientID + consumerClient, found := s.providerChain.App.(*appProvider.App).ProviderKeeper.GetConsumerClientId( + s.providerCtx(), + s.consumerChain.ChainID, + ) + s.Require().True(found, "consumer client not found") + s.path.EndpointB.ClientID = consumerClient + // - set consumer endpoint's clientID + providerClient, found := s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.GetProviderClient(s.consumerChain.GetContext()) + s.Require().True(found, "provider client not found") + s.path.EndpointA.ClientID = providerClient + // - client config + providerUnbondingPeriod := s.providerChain.App.(*appProvider.App).GetStakingKeeper().UnbondingTime(s.providerCtx()) + s.path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig).UnbondingPeriod = providerUnbondingPeriod + s.path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod = providerUnbondingPeriod / utils.TrustingPeriodFraction + consumerUnbondingPeriod := utils.ComputeConsumerUnbondingPeriod(providerUnbondingPeriod) + s.path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).UnbondingPeriod = consumerUnbondingPeriod + s.path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod = consumerUnbondingPeriod / utils.TrustingPeriodFraction + // - channel config + s.path.EndpointA.ChannelConfig.PortID = consumertypes.PortID + s.path.EndpointB.ChannelConfig.PortID = providertypes.PortID + s.path.EndpointA.ChannelConfig.Version = types.Version + s.path.EndpointB.ChannelConfig.Version = types.Version + s.path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED + s.path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED + + // set chains sender account number + // TODO: to be fixed in #151 + err = s.path.EndpointB.Chain.SenderAccount.SetAccountNumber(6) + s.Require().NoError(err) + err = s.path.EndpointA.Chain.SenderAccount.SetAccountNumber(0) + s.Require().NoError(err) + + // create path for the transfer channel + s.transferPath = ibctesting.NewPath(s.consumerChain, s.providerChain) + s.transferPath.EndpointA.ChannelConfig.PortID = transfertypes.PortID + s.transferPath.EndpointB.ChannelConfig.PortID = transfertypes.PortID + s.transferPath.EndpointA.ChannelConfig.Version = transfertypes.Version + s.transferPath.EndpointB.ChannelConfig.Version = transfertypes.Version +} + +func (s *ConsumerDemocracyTestSuite) SetupCCVChannel() { + s.StartSetupCCVChannel() + s.CompleteSetupCCVChannel() + s.SetupTransferChannel() +} + +func (s *ConsumerDemocracyTestSuite) StartSetupCCVChannel() { + s.coordinator.CreateConnections(s.path) + + err := s.path.EndpointA.ChanOpenInit() + s.Require().NoError(err) + + err = s.path.EndpointB.ChanOpenTry() + s.Require().NoError(err) +} + +func (s *ConsumerDemocracyTestSuite) CompleteSetupCCVChannel() { + err := s.path.EndpointA.ChanOpenAck() + s.Require().NoError(err) + + err = s.path.EndpointB.ChanOpenConfirm() + s.Require().NoError(err) + + // ensure counterparty is up to date + err = s.path.EndpointA.UpdateClient() + s.Require().NoError(err) +} + +func (s *ConsumerDemocracyTestSuite) SetupTransferChannel() { + // transfer path will use the same connection as ccv path + + s.transferPath.EndpointA.ClientID = s.path.EndpointA.ClientID + s.transferPath.EndpointA.ConnectionID = s.path.EndpointA.ConnectionID + s.transferPath.EndpointB.ClientID = s.path.EndpointB.ClientID + s.transferPath.EndpointB.ConnectionID = s.path.EndpointB.ConnectionID + + // CCV channel handshake will automatically initiate transfer channel handshake on ACK + // so transfer channel will be on stage INIT when CompleteSetupCCVChannel returns. + s.transferPath.EndpointA.ChannelID = s.consumerChain.App.(*appConsumer.App). + ConsumerKeeper.GetDistributionTransmissionChannel(s.consumerChain.GetContext()) + + // Complete TRY, ACK, CONFIRM for transfer path + err := s.transferPath.EndpointB.ChanOpenTry() + s.Require().NoError(err) + + err = s.transferPath.EndpointA.ChanOpenAck() + s.Require().NoError(err) + + err = s.transferPath.EndpointB.ChanOpenConfirm() + s.Require().NoError(err) + + // ensure counterparty is up to date + err = s.transferPath.EndpointA.UpdateClient() + s.Require().NoError(err) +} + +func TestConsumerDemocracyTestSuite(t *testing.T) { + suite.Run(t, new(ConsumerDemocracyTestSuite)) +} + +func (s *ConsumerDemocracyTestSuite) checkBalancesOverNextBlock() { + + stakingKeeper := s.consumerChain.App.(*appConsumer.App).StakingKeeper + authKeeper := s.consumerChain.App.(*appConsumer.App).AccountKeeper + distrKeeper := s.consumerChain.App.(*appConsumer.App).DistrKeeper + bankKeeper := s.consumerChain.App.(*appConsumer.App).BankKeeper + + distrAccount := distrKeeper.GetDistributionAccount(s.consumerCtx()) + + fmt.Printf("feeAccountName is %s\n", authtypes.FeeCollectorName) + fmt.Printf("consumerRedistributeAccount is %s\n", consumertypes.ConsumerRedistributeName) + fmt.Printf("providerRedistributeAccount is %s\n", consumertypes.ConsumerToSendToProviderName) + feeCollectorAccount := authKeeper.GetModuleAccount(s.consumerCtx(), authtypes.FeeCollectorName) + consumerRedistributeAccount := authKeeper.GetModuleAccount(s.consumerCtx(), consumertypes.ConsumerRedistributeName) + providerRedistributeAccount := authKeeper.GetModuleAccount(s.consumerCtx(), consumertypes.ConsumerToSendToProviderName) + + bondDenom := stakingKeeper.BondDenom(s.consumerCtx()) + + distrAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + feeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), feeCollectorAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + cosumerFeeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + providerFeeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + + fmt.Println("distrAccountBalance0", formatCommas(distrAccountBalance0)) + fmt.Println("feeAccountBalance0", formatCommas(feeAccountBalance0)) + fmt.Println("cosumerFeeAccountBalance0", formatCommas(cosumerFeeAccountBalance0)) + fmt.Println("providerFeeAccountBalance0", formatCommas(providerFeeAccountBalance0)) + + s.consumerChain.NextBlock() + + distrAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + feeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), feeCollectorAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + cosumerFeeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + providerFeeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) + + fmt.Println("distrAccountBalance1", formatCommas(distrAccountBalance1)) + fmt.Println("feeAccountBalance1", formatCommas(feeAccountBalance1)) + fmt.Println("cosumerFeeAccountBalance1", formatCommas(cosumerFeeAccountBalance1)) + fmt.Println("providerFeeAccountBalance1", formatCommas(providerFeeAccountBalance1)) + + distrDifference := distrAccountBalance1.Sub(distrAccountBalance0) + fmt.Println("distrDifference", formatCommas(distrDifference)) + + providerDifference := providerFeeAccountBalance1.Sub(providerFeeAccountBalance0) + fmt.Println("providerDifference", formatCommas(providerDifference)) + + ratio := providerFeeAccountBalance1.ToDec().QuoInt(distrAccountBalance1) + fmt.Println("ratio between distr and provider pool", ratio) +} + +func (s *ConsumerDemocracyTestSuite) TestPacketRoundtrip() { + s.SetupCCVChannel() + + s.checkBalancesOverNextBlock() + s.checkBalancesOverNextBlock() + s.checkBalancesOverNextBlock() + s.checkBalancesOverNextBlock() + + // s.consumerChain.NextBlock() + + // feeAccountBalance2 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom) + // cosumerFeeAccountBalance2 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom) + + // fmt.Println("feeAccountBalance2", feeAccountBalance2) + // fmt.Println("cosumerFeeAccountBalance2", cosumerFeeAccountBalance2) + + // feeAccountBalanceDifference = feeAccountBalance2.Sub(feeAccountBalance1) + // fmt.Println("feeAccountBalanceDifference", feeAccountBalanceDifference) + + // // providerCtx := s.providerChain.GetContext() + // providerStakingKeeper := s.providerChain.App.(*appProvider.App).StakingKeeper + + // origTime := s.providerCtx().BlockTime() + // bondAmt := sdk.NewInt(1000000) + + // delAddr := s.providerChain.SenderAccount.GetAddress() + + // // Choose a validator, and get its address and data structure into the correct types + // tmValidator := s.providerChain.Vals.Validators[0] + // valAddr, err := sdk.ValAddressFromHex(tmValidator.Address.String()) + // s.Require().NoError(err) + // validator, found := providerStakingKeeper.GetValidator(s.providerCtx(), valAddr) + // s.Require().True(found) + + // // Bond some tokens on provider to change validator powers + // _, err = providerStakingKeeper.Delegate(s.providerCtx(), delAddr, bondAmt, stakingtypes.Unbonded, stakingtypes.Validator(validator), true) + // s.Require().NoError(err) + + // // Save valset update ID to reconstruct packet + // valUpdateID := s.providerChain.App.(*appProvider.App).ProviderKeeper.GetValidatorSetUpdateId(s.providerCtx()) + + // // Send CCV packet to consumer + // s.providerChain.App.EndBlock(abci.RequestEndBlock{}) + + // // Get validator update created in Endblock to use in reconstructing packet + // valUpdates := providerStakingKeeper.GetValidatorUpdates(s.providerCtx()) + + // // commit block on provider chain and update consumer chain's client + // oldBlockTime := s.providerCtx().BlockTime() + // s.coordinator.CommitBlock(s.providerChain) + // err = s.path.EndpointA.UpdateClient() + // s.Require().NoError(err) + + // // Reconstruct packet + // packetData := types.NewValidatorSetChangePacketData(valUpdates, valUpdateID, nil) + // timeout := uint64(ccv.GetTimeoutTimestamp(oldBlockTime).UnixNano()) + // packet := channeltypes.NewPacket(packetData.GetBytes(), 1, providertypes.PortID, s.path.EndpointB.ChannelID, + // consumertypes.PortID, s.path.EndpointA.ChannelID, clienttypes.Height{}, timeout) + + // // Receive CCV packet on consumer chain + // err = s.path.EndpointA.RecvPacket(packet) + // s.Require().NoError(err) + + // // - End provider unbonding period + // // providerCtx = providerCtx.WithBlockTime(origTime.Add(providerStakingKeeper.UnbondingTime(s.providerCtx())).Add(3 * time.Hour)) + // s.providerChain.App.EndBlock(abci.RequestEndBlock{}) + + // // - End consumer unbonding period + // unbondingPeriod, found := s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.GetUnbondingTime(s.consumerCtx()) + // s.Require().True(found) + // consumerCtx := s.consumerCtx().WithBlockTime(origTime.Add(unbondingPeriod).Add(3 * time.Hour)) + // // TODO: why doesn't this work: s.consumerChain.App.EndBlock(abci.RequestEndBlock{}) + // err = s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.UnbondMaturePackets(consumerCtx) + // s.Require().NoError(err) + + // // commit consumer chain and update provider chain client + // s.coordinator.CommitBlock(s.consumerChain) + + // err = s.path.EndpointB.UpdateClient() + // s.Require().NoError(err) + + // ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + + // err = s.path.EndpointB.AcknowledgePacket(packet, ack.Acknowledgement()) + // s.Require().NoError(err) +} + +func (s *ConsumerDemocracyTestSuite) providerCtx() sdk.Context { + return s.providerChain.GetContext() +} + +func (s *ConsumerDemocracyTestSuite) consumerCtx() sdk.Context { + return s.consumerChain.GetContext() +} + +// func (s *ConsumerDemocracyTestSuite) providerBondDenom() string { +// return s.providerChain.App.(*appProvider.App).StakingKeeper.BondDenom(s.providerCtx()) +// } + +func formatCommas(numm sdk.Int) string { + num := numm.Int64() + str := fmt.Sprintf("%d", num) + re := regexp.MustCompile(`(\\d+)(\\d{3})`) + for n := ""; n != str; { + n = str + str = re.ReplaceAllString(str, "$1,$2") + } + return str +} diff --git a/x/ccv/democracy/distribution/doc.go b/x/ccv/democracy/distribution/doc.go new file mode 100644 index 0000000000..fd51ea0589 --- /dev/null +++ b/x/ccv/democracy/distribution/doc.go @@ -0,0 +1,9 @@ +/* +Package staking defines a "wrapper" module around the Cosmos SDK's native +x/distribution module. In other words, it provides the exact same functionality as +the native module in that it simply embeds the native module. + +The consumer chain should utilize the x/ccv/democracy/distribution module to perform democratic +actions such as participating and voting within the chain's governance system. +*/ +package distribution diff --git a/x/ccv/distribution/module.go b/x/ccv/democracy/distribution/module.go similarity index 94% rename from x/ccv/distribution/module.go rename to x/ccv/democracy/distribution/module.go index 6c384bb208..6591fc95f0 100644 --- a/x/ccv/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -4,6 +4,7 @@ import ( "time" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,6 +18,12 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + // AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. type AppModuleBasic struct { distr.AppModuleBasic @@ -54,7 +61,7 @@ func NewAppModule( // BeginBlocker mirror functionality of cosmos-sdk/distribution BeginBlocker // however it allocates no proposer reward -func (am AppModule) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) { +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { defer telemetry.ModuleMeasureSince(distrtypes.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) // TODO this is Tendermint-dependent diff --git a/x/ccv/staking/doc.go b/x/ccv/democracy/staking/doc.go similarity index 85% rename from x/ccv/staking/doc.go rename to x/ccv/democracy/staking/doc.go index 71c779783a..a405c6fa2d 100644 --- a/x/ccv/staking/doc.go +++ b/x/ccv/democracy/staking/doc.go @@ -6,7 +6,7 @@ overrides two core methods, `InitGenesis` and `EndBlock`. Specifically, these methods perform no-ops and return no validator set updates, as validator sets are tracked by the consumer chain's x/ccv/consumer module. -The consumer chain should utilize the x/ccv/staking module to perform democratic +The consumer chain should utilize the x/ccv/democracy/staking module to perform democratic actions such as participating and voting within the chain's governance system. */ package staking diff --git a/x/ccv/staking/module.go b/x/ccv/democracy/staking/module.go similarity index 100% rename from x/ccv/staking/module.go rename to x/ccv/democracy/staking/module.go diff --git a/x/ccv/distribution/doc.go b/x/ccv/distribution/doc.go deleted file mode 100644 index fdf2cbd284..0000000000 --- a/x/ccv/distribution/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -/* -Package distribution implements a Cosmos SDK module, that provides an implementation -of the F1 fee distribution algorithm. It handles reward tracking, allocation, and -distribution. Please refer to the specification under /spec for further information. - -This wrapped version of distribution excludes any proposer reward and is -intended to be used on a consumer chain. -*/ -package distribution From 7a3298c3af41339187371ac1bcc60da16e747f43 Mon Sep 17 00:00:00 2001 From: dusan-ethernal Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 08/13] Rebase and fix build errors --- Makefile | 4 ++-- app/consumer-democracy/app.go | 3 +-- x/ccv/democracy/democracy_test.go | 7 +++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index ca82e6ff30..3e992796b6 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ install: go.sum export GOFLAGS='-buildmode=pie' export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2" export CGO_LDFLAGS="-Wl,-z,relro,-z,now -fstack-protector" - # go install $(BUILD_FLAGS) ./cmd/interchain-security-pd - # go install $(BUILD_FLAGS) ./cmd/interchain-security-cd + go install $(BUILD_FLAGS) ./cmd/interchain-security-pd + go install $(BUILD_FLAGS) ./cmd/interchain-security-cd go install $(BUILD_FLAGS) ./cmd/interchain-security-cdd # run all tests: unit, e2e, diff, and integration diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 593534a6ab..6aacfffa02 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -259,7 +259,7 @@ func New( capabilitytypes.StoreKey, authzkeeper.StoreKey, ibcconsumertypes.StoreKey, ) - tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, ccvstakingtypes.TStoreKey) + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) app := &App{ @@ -334,7 +334,6 @@ func New( ccvstakingKeeper := ccvstakingkeeper.NewKeeper( appCodec, keys[ccvstakingtypes.StoreKey], - tkeys[ccvstakingtypes.TStoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(ccvstakingtypes.ModuleName), diff --git a/x/ccv/democracy/democracy_test.go b/x/ccv/democracy/democracy_test.go index 4085765286..b59cd86d8e 100644 --- a/x/ccv/democracy/democracy_test.go +++ b/x/ccv/democracy/democracy_test.go @@ -18,7 +18,6 @@ import ( appProvider "github.com/cosmos/interchain-security/app/provider" "github.com/cosmos/interchain-security/testutil/simapp" consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" - providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" "github.com/cosmos/interchain-security/x/ccv/types" "github.com/cosmos/interchain-security/x/ccv/utils" @@ -91,7 +90,7 @@ func (s *ConsumerDemocracyTestSuite) SetupTest() { s.Require().True(found, "consumer client not found") s.path.EndpointB.ClientID = consumerClient // - set consumer endpoint's clientID - providerClient, found := s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.GetProviderClient(s.consumerChain.GetContext()) + providerClient, found := s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.GetProviderClientID(s.consumerChain.GetContext()) s.Require().True(found, "provider client not found") s.path.EndpointA.ClientID = providerClient // - client config @@ -102,8 +101,8 @@ func (s *ConsumerDemocracyTestSuite) SetupTest() { s.path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).UnbondingPeriod = consumerUnbondingPeriod s.path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod = consumerUnbondingPeriod / utils.TrustingPeriodFraction // - channel config - s.path.EndpointA.ChannelConfig.PortID = consumertypes.PortID - s.path.EndpointB.ChannelConfig.PortID = providertypes.PortID + s.path.EndpointA.ChannelConfig.PortID = types.ConsumerPortID + s.path.EndpointB.ChannelConfig.PortID = types.ProviderPortID s.path.EndpointA.ChannelConfig.Version = types.Version s.path.EndpointB.ChannelConfig.Version = types.Version s.path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED From 25fbc82392107fc10e64f367cde003debcf115ce Mon Sep 17 00:00:00 2001 From: dusan-ethernal Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 09/13] Democracy chain integration tests, part 1 --- Dockerfile | 1 + .../ante/msg_filter_ante.go | 1 + app/consumer-democracy/ante_handler.go | 1 + tests/integration/actions.go | 223 +++++++++++- tests/integration/config.go | 11 + tests/integration/main.go | 19 +- tests/integration/state.go | 102 +++++- tests/integration/steps_democracy.go | 329 ++++++++++++++++++ x/ccv/democracy/democracy_test.go | 1 + 9 files changed, 679 insertions(+), 9 deletions(-) create mode 100644 tests/integration/steps_democracy.go diff --git a/Dockerfile b/Dockerfile index 7b699ac974..cc537c7ccc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,6 +39,7 @@ COPY --from=hermes-builder /usr/bin/hermes /usr/local/bin/ COPY --from=is-builder /go/bin/interchain-security-pd /usr/local/bin/interchain-security-pd COPY --from=is-builder /go/bin/interchain-security-cd /usr/local/bin/interchain-security-cd +COPY --from=is-builder /go/bin/interchain-security-cdd /usr/local/bin/interchain-security-cdd # Copy in the shell scripts that run the testnet diff --git a/app/consumer-democracy/ante/msg_filter_ante.go b/app/consumer-democracy/ante/msg_filter_ante.go index 5788f12a5e..a9b2b2bd78 100644 --- a/app/consumer-democracy/ante/msg_filter_ante.go +++ b/app/consumer-democracy/ante/msg_filter_ante.go @@ -22,6 +22,7 @@ type ( } ) +// TODO Ethernal: remove this, it is duplicated (consumerante does the same thing) func NewMsgFilterDecorator(k ConsumerKeeper) MsgFilterDecorator { return MsgFilterDecorator{ ConsumerKeeper: k, diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go index ea0984b41b..557a245756 100644 --- a/app/consumer-democracy/ante_handler.go +++ b/app/consumer-democracy/ante_handler.go @@ -39,6 +39,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), consumerante.NewMsgFilterDecorator(options.ConsumerKeeper), + // TODO Ethernal add handler to disable slashing and evidence messages (issue 115) ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), diff --git a/tests/integration/actions.go b/tests/integration/actions.go index 955e6f481d..f7b6444819 100644 --- a/tests/integration/actions.go +++ b/tests/integration/actions.go @@ -257,6 +257,77 @@ func (tr TestRun) submitConsumerProposal( } } +type submitParamChangeProposalAction struct { + chain chainID + from validatorID + deposit uint + subspace string + key string + value interface{} +} + +type paramChangeProposalJSON struct { + Title string `json:"title"` + Description string `json:"description"` + Changes []paramChangeJSON `json:"changes"` + Deposit string `json:"deposit"` +} + +type paramChangeJSON struct { + Subspace string `json:"subspace"` + Key string `json:"key"` + Value interface{} `json:"value"` +} + +func (tr TestRun) submitParamChangeProposal( + action submitParamChangeProposalAction, + verbose bool, +) { + prop := paramChangeProposalJSON{ + Title: "Param change", + Description: "Changing module params", + Changes: []paramChangeJSON{{Subspace: action.subspace, Key: action.key, Value: action.value}}, + Deposit: fmt.Sprint(action.deposit) + `stake`, + } + + bz, err := json.Marshal(prop) + if err != nil { + log.Fatal(err) + } + + jsonStr := string(bz) + if strings.Contains(jsonStr, "'") { + log.Fatal("prop json contains single quote") + } + + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err = exec.Command("docker", "exec", tr.containerConfig.instanceName, + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/params-proposal.json")).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err = exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, + + "tx", "gov", "submit-proposal", "param-change", + "/params-proposal.json", + + `--from`, `validator`+fmt.Sprint(action.from), + `--chain-id`, string(tr.chainConfigs[action.chain].chainId), + `--home`, tr.getValidatorHome(action.chain, action.from), + `--node`, tr.getValidatorNode(action.chain, action.from), + `--keyring-backend`, `test`, + `-b`, `block`, + `-y`, + ).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } +} + type voteGovProposalAction struct { chain chainID from []validatorID @@ -300,9 +371,10 @@ func (tr TestRun) voteGovProposal( } type startConsumerChainAction struct { - consumerChain chainID - providerChain chainID - validators []StartChainValidator + consumerChain chainID + providerChain chainID + genesisChanges string + validators []StartChainValidator } func (tr TestRun) startConsumerChain( @@ -329,10 +401,15 @@ func (tr TestRun) startConsumerChain( log.Fatal(err, "\n", string(bz)) } + genesisChanges := ".app_state.ccvconsumer = " + string(bz) + if action.genesisChanges != "" { + genesisChanges = genesisChanges + " | " + action.genesisChanges + } + tr.startChain(StartChainAction{ chain: action.consumerChain, validators: action.validators, - genesisChanges: ".app_state.ccvconsumer = " + string(bz), + genesisChanges: genesisChanges, skipGentx: true, }, verbose) } @@ -518,6 +595,88 @@ func (tr TestRun) addIbcChannel( } } +type transferChannelCompleteAction struct { + chainA chainID + chainB chainID + connectionA uint + portA string + portB string + order string + channelA uint + channelB uint +} + +func (tr TestRun) transferChannelComplete( + action transferChannelCompleteAction, + verbose bool, +) { + //#nosec G204 -- Bypass linter warning for spawning subprocess with chanOpenTryCmd arguments. + chanOpenTryCmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "hermes", + "tx", "chan-open-try", + "--dst-chain", string(tr.chainConfigs[action.chainB].chainId), + "--src-chain", string(tr.chainConfigs[action.chainA].chainId), + "--dst-connection", "connection-"+fmt.Sprint(action.connectionA), + "--dst-port", action.portB, + "--src-port", action.portA, + "--src-channel", "channel-"+fmt.Sprint(action.channelA), + ) + executeCommand(chanOpenTryCmd, "transferChanOpenTry") + + //#nosec G204 -- Bypass linter warning for spawning subprocess with chanOpenAckCmd arguments. + chanOpenAckCmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "hermes", + "tx", "chan-open-ack", + "--dst-chain", string(tr.chainConfigs[action.chainA].chainId), + "--src-chain", string(tr.chainConfigs[action.chainB].chainId), + "--dst-connection", "connection-"+fmt.Sprint(action.connectionA), + "--dst-port", action.portA, + "--src-port", action.portB, + "--dst-channel", "channel-"+fmt.Sprint(action.channelA), + "--src-channel", "channel-"+fmt.Sprint(action.channelB), + ) + executeCommand(chanOpenAckCmd, "transferChanOpenAck") + + //#nosec G204 -- Bypass linter warning for spawning subprocess with chanOpenConfirmCmd arguments. + chanOpenConfirmCmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, "hermes", + "tx", "chan-open-confirm", + "--dst-chain", string(tr.chainConfigs[action.chainB].chainId), + "--src-chain", string(tr.chainConfigs[action.chainA].chainId), + "--dst-connection", "connection-"+fmt.Sprint(action.connectionA), + "--dst-port", action.portB, + "--src-port", action.portA, + "--dst-channel", "channel-"+fmt.Sprint(action.channelB), + "--src-channel", "channel-"+fmt.Sprint(action.channelA), + ) + executeCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") +} + +func executeCommand(cmd *exec.Cmd, cmdName string) { + if verbose { + fmt.Println(cmdName+" cmd:", cmd.String()) + } + + cmdReader, err := cmd.StdoutPipe() + if err != nil { + log.Fatal(err) + } + cmd.Stderr = cmd.Stdout + + if err := cmd.Start(); err != nil { + log.Fatal(err) + } + + scanner := bufio.NewScanner(cmdReader) + + for scanner.Scan() { + out := scanner.Text() + if verbose { + fmt.Println(cmdName + ": " + out) + } + } + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} + type relayPacketsAction struct { chain chainID port string @@ -736,3 +895,59 @@ func (tr TestRun) unjailValidator(action unjailValidatorAction, verbose bool) { log.Fatal(err, "\n", string(bz)) } } + +type registerRepresentAction struct { + chain chainID + represents []validatorID + stakes []uint +} + +func (tr TestRun) registerRepresent( + action registerRepresentAction, + verbose bool, +) { + var wg sync.WaitGroup + for i, val := range action.represents { + wg.Add(1) + stake := action.stakes[i] + go func(val validatorID, stake uint) { + defer wg.Done() + + //#nosec G204 -- Bypass linter warning for spawning subprocess with pubKeycmd arguments. + pubKeycmd := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, + "tendermint", "show-validator", + `--home`, tr.getValidatorHome(action.chain, val), + ) + + bzPubKey, err := pubKeycmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bzPubKey)) + } + + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[action.chain].binaryName, + "tx", "staking", "create-validator", + `--amount`, fmt.Sprint(stake)+"stake", + `--pubkey`, string(bzPubKey), + `--moniker`, fmt.Sprint(val), + `--commission-rate`, "0.1", + `--commission-max-rate`, "0.2", + `--commission-max-change-rate`, "0.01", + `--min-self-delegation`, "1", + `--from`, `validator`+fmt.Sprint(val), + `--chain-id`, string(tr.chainConfigs[action.chain].chainId), + `--home`, tr.getValidatorHome(action.chain, val), + `--node`, tr.getValidatorNode(action.chain, val), + `--keyring-backend`, `test`, + `-b`, `block`, + `-y`, + ).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + }(val, stake) + } + + wg.Wait() +} diff --git a/tests/integration/config.go b/tests/integration/config.go index 9fb890e246..ffebd002dc 100644 --- a/tests/integration/config.go +++ b/tests/integration/config.go @@ -116,6 +116,17 @@ func DefaultTestRun() TestRun { ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", }, + chainID("democ"): { + chainId: chainID("democ"), + binaryName: "interchain-security-cdd", + ipPrefix: "7.7.9", + votingWaitTime: 10, + genesisChanges: ".app_state.gov.voting_params.voting_period = \"10s\" | " + + ".app_state.slashing.params.signed_blocks_window = \"2\" | " + + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + + ".app_state.slashing.params.downtime_jail_duration = \"2s\" | " + + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", + }, }, } } diff --git a/tests/integration/main.go b/tests/integration/main.go index 67a4b35f48..432291fd59 100644 --- a/tests/integration/main.go +++ b/tests/integration/main.go @@ -14,6 +14,7 @@ import ( var verbose = true func main() { + fmt.Println("============================================ start happy path tests ============================================") start := time.Now() tr := DefaultTestRun() tr.ParseCLIFlags() @@ -24,7 +25,17 @@ func main() { tr.runStep(step, verbose) } - fmt.Printf("test successful - time elapsed %v\n", time.Since(start)) + fmt.Printf("happy path tests successful - time elapsed %v\n", time.Since(start)) + + fmt.Println("============================================ start democracy path tests ============================================") + start = time.Now() + tr.startDocker() + + for _, step := range democracySteps { + tr.runStep(step, verbose) + } + + fmt.Printf("democracy path tests successful - time elapsed %v\n", time.Since(start)) } func (tr TestRun) runStep(step Step, verbose bool) { @@ -38,6 +49,8 @@ func (tr TestRun) runStep(step Step, verbose bool) { tr.submitTextProposal(action, verbose) case submitConsumerProposalAction: tr.submitConsumerProposal(action, verbose) + case submitParamChangeProposalAction: + tr.submitParamChangeProposal(action, verbose) case voteGovProposalAction: tr.voteGovProposal(action, verbose) case startConsumerChainAction: @@ -48,6 +61,8 @@ func (tr TestRun) runStep(step Step, verbose bool) { tr.addIbcConnection(action, verbose) case addIbcChannelAction: tr.addIbcChannel(action, verbose) + case transferChannelCompleteAction: + tr.transferChannelComplete(action, verbose) case relayPacketsAction: tr.relayPackets(action, verbose) case delegateTokensAction: @@ -60,6 +75,8 @@ func (tr TestRun) runStep(step Step, verbose bool) { tr.invokeDowntimeSlash(action, verbose) case unjailValidatorAction: tr.unjailValidator(action, verbose) + case registerRepresentAction: + tr.registerRepresent(action, verbose) default: log.Fatalf(fmt.Sprintf(`unknown action: %#v`, action)) } diff --git a/tests/integration/state.go b/tests/integration/state.go index b8768c6963..ab74b95f46 100644 --- a/tests/integration/state.go +++ b/tests/integration/state.go @@ -17,9 +17,11 @@ import ( type State map[chainID]ChainState type ChainState struct { - ValBalances *map[validatorID]uint - Proposals *map[uint]Proposal - ValPowers *map[validatorID]uint + ValBalances *map[validatorID]uint + Proposals *map[uint]Proposal + ValPowers *map[validatorID]uint + RepresentPowers *map[validatorID]uint + Params *[]Param } type Proposal interface { @@ -44,6 +46,22 @@ type ConsumerProposal struct { func (p ConsumerProposal) isProposal() {} +type ParamsProposal struct { + Deposit uint + Status string + Subspace string + Key string + Value string +} + +func (p ParamsProposal) isProposal() {} + +type Param struct { + Subspace string + Key string + Value string +} + func (tr TestRun) getState(modelState State) State { systemState := State{} for k, modelState := range modelState { @@ -72,6 +90,16 @@ func (tr TestRun) getChainState(chain chainID, modelState ChainState) ChainState chainState.ValPowers = &powers } + if modelState.RepresentPowers != nil { + representPowers := tr.getRepresentPowers(chain, *modelState.RepresentPowers) + chainState.RepresentPowers = &representPowers + } + + if modelState.Params != nil { + params := tr.getParams(chain, *modelState.Params) + chainState.Params = ¶ms + } + return chainState } @@ -141,6 +169,24 @@ func (tr TestRun) getValPowers(chain chainID, modelState map[validatorID]uint) m return actualState } +func (tr TestRun) getRepresentPowers(chain chainID, modelState map[validatorID]uint) map[validatorID]uint { + actualState := map[validatorID]uint{} + for k := range modelState { + actualState[k] = tr.getRepresentPower(chain, k) + } + + return actualState +} + +func (tr TestRun) getParams(chain chainID, modelState []Param) []Param { + actualState := []Param{} + for _, p := range modelState { + actualState = append(actualState, Param{Subspace: p.Subspace, Key: p.Key, Value: tr.getParam(chain, p)}) + } + + return actualState +} + func (tr TestRun) getBalance(chain chainID, validator validatorID) uint { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, @@ -222,7 +268,14 @@ func (tr TestRun) getProposal(chain chainID, proposal uint) Proposal { RevisionHeight: gjson.Get(string(bz), `content.initial_height.revision_height`).Uint(), }, } - + case "/cosmos.params.v1beta1.ParameterChangeProposal": + return ParamsProposal{ + Deposit: uint(deposit), + Status: status, + Subspace: gjson.Get(string(bz), `content.changes.0.subspace`).String(), + Key: gjson.Get(string(bz), `content.changes.0.key`).String(), + Value: gjson.Get(string(bz), `content.changes.0.value`).String(), + } } log.Fatal("unknown proposal type", string(bz)) @@ -288,6 +341,47 @@ func (tr TestRun) getValPower(chain chainID, validator validatorID) uint { return 0 } +func (tr TestRun) getRepresentPower(chain chainID, validator validatorID) uint { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, + + "query", "staking", "validator", + tr.validatorConfigs[validator].valoperAddress, + + `--node`, tr.getValidatorNode(chain, tr.getDefaultValidator(chain)), + `-o`, `json`, + ).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + amount := gjson.Get(string(bz), `tokens`) + + return uint(amount.Uint()) +} + +func (tr TestRun) getParam(chain chainID, param Param) string { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, + + "query", "params", "subspace", + param.Subspace, + param.Key, + + `--node`, tr.getValidatorNode(chain, tr.getDefaultValidator(chain)), + `-o`, `json`, + ).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + value := gjson.Get(string(bz), `value`) + + return value.String() +} + // Gets a default validator for txs and queries using the first subdirectory // of the directory of the input chain, which will be the home directory // of one of the validators. diff --git a/tests/integration/steps_democracy.go b/tests/integration/steps_democracy.go new file mode 100644 index 0000000000..c474ee0d13 --- /dev/null +++ b/tests/integration/steps_democracy.go @@ -0,0 +1,329 @@ +package main + +import ( + clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" +) + +var democracySteps = []Step{ + { + action: StartChainAction{ + chain: chainID("provi"), + validators: []StartChainValidator{ + {id: validatorID("bob"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("carol"), stake: 500000000, allocation: 10000000000}, + }, + genesisChanges: "", // No custom genesis changes for this action + skipGentx: false, + }, + state: State{ + chainID("provi"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9500000000, + validatorID("bob"): 9500000000, + }, + }, + }, + }, + { + action: SendTokensAction{ + chain: chainID("provi"), + from: validatorID("alice"), + to: validatorID("bob"), + amount: 2, + }, + state: State{ + chainID("provi"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9499999998, + validatorID("bob"): 9500000002, + }, + }, + }, + }, + { + action: submitConsumerProposalAction{ + chain: chainID("provi"), + from: validatorID("alice"), + deposit: 10000001, + consumerChain: chainID("democ"), + spawnTime: 0, + initialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + }, + state: State{ + chainID("provi"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9489999997, + validatorID("bob"): 9500000002, + }, + Proposals: &map[uint]Proposal{ + 1: ConsumerProposal{ + Deposit: 10000001, + Chain: chainID("democ"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + action: voteGovProposalAction{ + chain: chainID("provi"), + from: []validatorID{validatorID("alice"), validatorID("bob"), validatorID("carol")}, + vote: []string{"yes", "yes", "yes"}, + propNumber: 1, + }, + state: State{ + chainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerProposal{ + Deposit: 10000001, + Chain: chainID("democ"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9499999998, + validatorID("bob"): 9500000002, + }, + }, + }, + }, + { + action: startConsumerChainAction{ + consumerChain: chainID("democ"), + providerChain: chainID("provi"), + genesisChanges: ".app_state.ccvconsumer.params.blocks_per_distribution_transmission = \"50\"", + validators: []StartChainValidator{ + {id: validatorID("carol"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, + {id: validatorID("bob"), stake: 500000000, allocation: 10000000000}, + }, + }, + state: State{ + chainID("provi"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9499999998, + validatorID("bob"): 9500000002, + }, + }, + chainID("democ"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 10000000000, + validatorID("bob"): 10000000000, + }, + }, + }, + }, + { + action: SendTokensAction{ + chain: chainID("democ"), + from: validatorID("alice"), + to: validatorID("bob"), + amount: 1, + }, + state: State{ + chainID("democ"): ChainState{ + // Tx on consumer chain should not go through before ICS channel is setup + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 10000000000, + validatorID("bob"): 10000000000, + }, + }, + }, + }, + { + action: addIbcConnectionAction{ + chainA: chainID("democ"), + chainB: chainID("provi"), + clientA: 0, + clientB: 0, + order: "ordered", + }, + state: State{}, + }, + { + action: addIbcChannelAction{ + chainA: chainID("democ"), + chainB: chainID("provi"), + connectionA: 0, + portA: "consumer", + portB: "provider", + order: "ordered", + }, + state: State{}, + }, + { + action: transferChannelCompleteAction{ + chainA: chainID("democ"), + chainB: chainID("provi"), + connectionA: 0, + portA: "transfer", + portB: "transfer", + order: "unordered", + channelA: 1, + channelB: 1, + }, + state: State{}, + }, + { + action: delegateTokensAction{ + chain: chainID("provi"), + from: validatorID("alice"), + to: validatorID("alice"), + amount: 11000000, + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 500, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: SendTokensAction{ + chain: chainID("democ"), + from: validatorID("alice"), + to: validatorID("bob"), + amount: 1, + }, + state: State{ + chainID("democ"): ChainState{ + // Tx should not go through, ICS channel is not setup until first VSC packet has been relayed to consumer + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 10000000000, + validatorID("bob"): 10000000000, + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: SendTokensAction{ + chain: chainID("democ"), + from: validatorID("alice"), + to: validatorID("bob"), + amount: 1, + }, + state: State{ + chainID("democ"): ChainState{ + // Now tx should execute + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9999999999, + validatorID("bob"): 10000000001, + }, + }, + }, + }, + // sanity checks end here + { + action: registerRepresentAction{ + chain: chainID("democ"), + represents: []validatorID{validatorID("alice"), validatorID("bob")}, + stakes: []uint{100000000, 40000000}, + }, + state: State{ + chainID("democ"): ChainState{ + RepresentPowers: &map[validatorID]uint{ + validatorID("alice"): 100000000, + validatorID("bob"): 40000000, + }, + }, + }, + }, + { + action: delegateTokensAction{ + chain: chainID("democ"), + from: validatorID("carol"), + to: validatorID("alice"), + amount: 500000, + }, + state: State{ + chainID("democ"): ChainState{ + RepresentPowers: &map[validatorID]uint{ + validatorID("alice"): 100500000, + validatorID("bob"): 40000000, + }, + // Check that delegating on gov-consumer does not change validator powers + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: submitParamChangeProposalAction{ + chain: chainID("democ"), + from: validatorID("alice"), + deposit: 10000001, + subspace: "staking", + key: "MaxValidators", + value: 105, + }, + state: State{ + chainID("democ"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9889999998, + validatorID("bob"): 9960000001, + }, + Proposals: &map[uint]Proposal{ + 1: ParamsProposal{ + Deposit: 10000001, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + Subspace: "staking", + Key: "MaxValidators", + Value: "105", + }, + }, + }, + }, + }, + { + action: voteGovProposalAction{ + chain: chainID("democ"), + from: []validatorID{validatorID("alice"), validatorID("bob")}, + vote: []string{"yes", "no"}, + propNumber: 1, + }, + state: State{ + chainID("democ"): ChainState{ + ValBalances: &map[validatorID]uint{ + validatorID("alice"): 9899999999, + validatorID("bob"): 9960000001, + }, + Params: &([]Param{{Subspace: "staking", Key: "MaxValidators", Value: "105"}}), + }, + }, + }, +} diff --git a/x/ccv/democracy/democracy_test.go b/x/ccv/democracy/democracy_test.go index b59cd86d8e..c3dafeda8f 100644 --- a/x/ccv/democracy/democracy_test.go +++ b/x/ccv/democracy/democracy_test.go @@ -1,5 +1,6 @@ package democracy_test +// TODO Ethernal: move to e2e folder, remove commented code import ( "bytes" "fmt" From d104915733e40e760b9a80cb43413d5ad43a1423 Mon Sep 17 00:00:00 2001 From: stana-ethernal Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 10/13] Democracy chain integration tests, part 2 --- tests/integration/actions.go | 36 ++++-- tests/integration/main.go | 6 +- tests/integration/state.go | 80 +++++++++++-- tests/integration/steps_democracy.go | 170 +++++++++++++++++++++++++-- 4 files changed, 265 insertions(+), 27 deletions(-) diff --git a/tests/integration/actions.go b/tests/integration/actions.go index f7b6444819..693176b880 100644 --- a/tests/integration/actions.go +++ b/tests/integration/actions.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "os/exec" + "strconv" "strings" "sync" "time" @@ -704,6 +705,27 @@ func (tr TestRun) relayPackets( } } +type relayRewardPacketsToProviderAction struct { + consumerChain chainID + providerChain chainID + port string + channel uint +} + +func (tr TestRun) relayRewardPacketsToProvider( + action relayRewardPacketsToProviderAction, + verbose bool, +) { + blockPerDistribution, _ := strconv.ParseUint(strings.Trim(tr.getParam(action.consumerChain, Param{Subspace: "ccvconsumer", Key: "BlocksPerDistributionTransmission"}), "\""), 10, 64) + currentBlock := uint64(tr.getBlockHeight(action.consumerChain)) + if currentBlock <= blockPerDistribution { + tr.waitBlocks(action.consumerChain, uint(blockPerDistribution-currentBlock+1), 60*time.Second) + } + + tr.relayPackets(relayPacketsAction{chain: action.consumerChain, port: action.port, channel: action.channel}, verbose) + tr.waitBlocks(action.providerChain, 1, 10*time.Second) +} + type delegateTokensAction struct { chain chainID from validatorID @@ -896,18 +918,18 @@ func (tr TestRun) unjailValidator(action unjailValidatorAction, verbose bool) { } } -type registerRepresentAction struct { - chain chainID - represents []validatorID - stakes []uint +type registerRepresentativeAction struct { + chain chainID + representatives []validatorID + stakes []uint } -func (tr TestRun) registerRepresent( - action registerRepresentAction, +func (tr TestRun) registerRepresentative( + action registerRepresentativeAction, verbose bool, ) { var wg sync.WaitGroup - for i, val := range action.represents { + for i, val := range action.representatives { wg.Add(1) stake := action.stakes[i] go func(val validatorID, stake uint) { diff --git a/tests/integration/main.go b/tests/integration/main.go index 432291fd59..66fff40cb9 100644 --- a/tests/integration/main.go +++ b/tests/integration/main.go @@ -65,6 +65,8 @@ func (tr TestRun) runStep(step Step, verbose bool) { tr.transferChannelComplete(action, verbose) case relayPacketsAction: tr.relayPackets(action, verbose) + case relayRewardPacketsToProviderAction: + tr.relayRewardPacketsToProvider(action, verbose) case delegateTokensAction: tr.delegateTokens(action, verbose) case unbondTokensAction: @@ -75,8 +77,8 @@ func (tr TestRun) runStep(step Step, verbose bool) { tr.invokeDowntimeSlash(action, verbose) case unjailValidatorAction: tr.unjailValidator(action, verbose) - case registerRepresentAction: - tr.registerRepresent(action, verbose) + case registerRepresentativeAction: + tr.registerRepresentative(action, verbose) default: log.Fatalf(fmt.Sprintf(`unknown action: %#v`, action)) } diff --git a/tests/integration/state.go b/tests/integration/state.go index ab74b95f46..88a659f71b 100644 --- a/tests/integration/state.go +++ b/tests/integration/state.go @@ -17,11 +17,12 @@ import ( type State map[chainID]ChainState type ChainState struct { - ValBalances *map[validatorID]uint - Proposals *map[uint]Proposal - ValPowers *map[validatorID]uint - RepresentPowers *map[validatorID]uint - Params *[]Param + ValBalances *map[validatorID]uint + Proposals *map[uint]Proposal + ValPowers *map[validatorID]uint + RepresentativePowers *map[validatorID]uint + Params *[]Param + Rewards *Rewards } type Proposal interface { @@ -44,6 +45,16 @@ type ConsumerProposal struct { Status string } +type Rewards struct { + IsRewarded map[validatorID]bool + //if true it will calculate if the validator/delegator is rewarded between 2 successive blocks, + //otherwise it will calculate if it received any rewards since the 1st block + IsIncrementalReward bool + //if true checks rewards for "stake" token, otherwise checks rewards from + //other chains (e.g. false is used to check if provider received rewards from a consumer chain) + IsNativeDenom bool +} + func (p ConsumerProposal) isProposal() {} type ParamsProposal struct { @@ -90,9 +101,9 @@ func (tr TestRun) getChainState(chain chainID, modelState ChainState) ChainState chainState.ValPowers = &powers } - if modelState.RepresentPowers != nil { - representPowers := tr.getRepresentPowers(chain, *modelState.RepresentPowers) - chainState.RepresentPowers = &representPowers + if modelState.RepresentativePowers != nil { + representPowers := tr.getRepresentativePowers(chain, *modelState.RepresentativePowers) + chainState.RepresentativePowers = &representPowers } if modelState.Params != nil { @@ -100,6 +111,11 @@ func (tr TestRun) getChainState(chain chainID, modelState ChainState) ChainState chainState.Params = ¶ms } + if modelState.Rewards != nil { + rewards := tr.getRewards(chain, *modelState.Rewards) + chainState.Rewards = &rewards + } + return chainState } @@ -169,10 +185,10 @@ func (tr TestRun) getValPowers(chain chainID, modelState map[validatorID]uint) m return actualState } -func (tr TestRun) getRepresentPowers(chain chainID, modelState map[validatorID]uint) map[validatorID]uint { +func (tr TestRun) getRepresentativePowers(chain chainID, modelState map[validatorID]uint) map[validatorID]uint { actualState := map[validatorID]uint{} for k := range modelState { - actualState[k] = tr.getRepresentPower(chain, k) + actualState[k] = tr.getRepresentativePower(chain, k) } return actualState @@ -187,6 +203,48 @@ func (tr TestRun) getParams(chain chainID, modelState []Param) []Param { return actualState } +func (tr TestRun) getRewards(chain chainID, modelState Rewards) Rewards { + receivedRewards := map[validatorID]bool{} + + currentBlock := tr.getBlockHeight(chain) + tr.waitBlocks(chain, 1, 10*time.Second) + nextBlock := tr.getBlockHeight(chain) + tr.waitBlocks(chain, 1, 10*time.Second) + + if !modelState.IsIncrementalReward { + currentBlock = 1 + } + for k := range modelState.IsRewarded { + receivedRewards[k] = tr.getReward(chain, k, nextBlock, modelState.IsNativeDenom) > tr.getReward(chain, k, currentBlock, modelState.IsNativeDenom) + } + + return Rewards{IsRewarded: receivedRewards, IsIncrementalReward: modelState.IsIncrementalReward, IsNativeDenom: modelState.IsNativeDenom} +} + +func (tr TestRun) getReward(chain chainID, validator validatorID, blockHeight uint, isNativeDenom bool) float64 { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, + + "query", "distribution", "rewards", + tr.validatorConfigs[validator].delAddress, + + `--height`, fmt.Sprint(blockHeight), + `--node`, tr.getValidatorNode(chain, tr.getDefaultValidator(chain)), + `-o`, `json`, + ).CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + denomCondition := `total.#(denom!="stake").amount` + if isNativeDenom { + denomCondition = `total.#(denom=="stake").amount` + } + + return gjson.Get(string(bz), denomCondition).Float() +} + func (tr TestRun) getBalance(chain chainID, validator validatorID) uint { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, @@ -341,7 +399,7 @@ func (tr TestRun) getValPower(chain chainID, validator validatorID) uint { return 0 } -func (tr TestRun) getRepresentPower(chain chainID, validator validatorID) uint { +func (tr TestRun) getRepresentativePower(chain chainID, validator validatorID) uint { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. bz, err := exec.Command("docker", "exec", tr.containerConfig.instanceName, tr.chainConfigs[chain].binaryName, diff --git a/tests/integration/steps_democracy.go b/tests/integration/steps_democracy.go index c474ee0d13..f442ab377b 100644 --- a/tests/integration/steps_democracy.go +++ b/tests/integration/steps_democracy.go @@ -97,7 +97,7 @@ var democracySteps = []Step{ action: startConsumerChainAction{ consumerChain: chainID("democ"), providerChain: chainID("provi"), - genesisChanges: ".app_state.ccvconsumer.params.blocks_per_distribution_transmission = \"50\"", + genesisChanges: ".app_state.ccvconsumer.params.blocks_per_distribution_transmission = \"10\"", validators: []StartChainValidator{ {id: validatorID("carol"), stake: 500000000, allocation: 10000000000}, {id: validatorID("alice"), stake: 500000000, allocation: 10000000000}, @@ -246,17 +246,26 @@ var democracySteps = []Step{ }, // sanity checks end here { - action: registerRepresentAction{ - chain: chainID("democ"), - represents: []validatorID{validatorID("alice"), validatorID("bob")}, - stakes: []uint{100000000, 40000000}, + action: registerRepresentativeAction{ + chain: chainID("democ"), + representatives: []validatorID{validatorID("alice"), validatorID("bob")}, + stakes: []uint{100000000, 40000000}, }, state: State{ chainID("democ"): ChainState{ - RepresentPowers: &map[validatorID]uint{ + RepresentativePowers: &map[validatorID]uint{ validatorID("alice"): 100000000, validatorID("bob"): 40000000, }, + Rewards: &Rewards{ + IsRewarded: map[validatorID]bool{ + validatorID("alice"): true, + validatorID("bob"): true, + validatorID("carol"): false, + }, + IsIncrementalReward: true, + IsNativeDenom: true, + }, }, }, }, @@ -269,7 +278,8 @@ var democracySteps = []Step{ }, state: State{ chainID("democ"): ChainState{ - RepresentPowers: &map[validatorID]uint{ + //Check that delegators on gov-consumer chain can change representative powers + RepresentativePowers: &map[validatorID]uint{ validatorID("alice"): 100500000, validatorID("bob"): 40000000, }, @@ -279,6 +289,16 @@ var democracySteps = []Step{ validatorID("bob"): 500, validatorID("carol"): 500, }, + //Check that tokens are minted and distributed to representatives and their delegators + Rewards: &Rewards{ + IsRewarded: map[validatorID]bool{ + validatorID("alice"): true, + validatorID("bob"): true, + validatorID("carol"): true, + }, + IsIncrementalReward: true, + IsNativeDenom: true, + }, }, }, }, @@ -310,6 +330,7 @@ var democracySteps = []Step{ }, }, { + //Have accounts vote on something on the gov-consumer chain action: voteGovProposalAction{ chain: chainID("democ"), from: []validatorID{validatorID("alice"), validatorID("bob")}, @@ -322,8 +343,143 @@ var democracySteps = []Step{ validatorID("alice"): 9899999999, validatorID("bob"): 9960000001, }, + //Check that the parameter is changed on gov-consumer chain Params: &([]Param{{Subspace: "staking", Key: "MaxValidators", Value: "105"}}), }, }, }, + { + action: relayRewardPacketsToProviderAction{ + consumerChain: chainID("democ"), + providerChain: chainID("provi"), + port: "transfer", + channel: 1, + }, + state: State{ + chainID("provi"): ChainState{ + //Check that tokens are minted and sent to provider chain and distributed to validators and their delegators on provider chain + Rewards: &Rewards{ + IsRewarded: map[validatorID]bool{ + validatorID("alice"): true, + validatorID("bob"): true, + validatorID("carol"): true, + }, + IsIncrementalReward: false, + IsNativeDenom: false, + }, + }, + }, + }, + { + action: downtimeSlashAction{ + chain: chainID("democ"), + // TODO: First validator cannot be brought down until this issue is resolved: + // https://github.com/cosmos/interchain-security/issues/263 + validator: validatorID("bob"), + }, + state: State{ + // validator should be slashed on consumer, powers not affected on either chain yet + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + // Downtime jailing and corresponding voting power change are processed by provider + validatorID("bob"): 0, + validatorID("carol"): 500, + }, + }, + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 500, + validatorID("carol"): 500, + }, + }, + }, + }, + // A block is incremented each action, hence why VSC is committed on provider, + // and can now be relayed as packet to consumer + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + // VSC now seen on consumer + validatorID("bob"): 0, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: unjailValidatorAction{ + provider: chainID("provi"), + validator: validatorID("bob"), + }, + state: State{ + chainID("provi"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + // 1% of bob's stake should be slashed as set in config.go + validatorID("bob"): 495, + validatorID("carol"): 500, + }, + }, + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 0, + validatorID("carol"): 500, + }, + }, + }, + }, + { + action: relayPacketsAction{ + chain: chainID("provi"), + port: "provider", + channel: 0, + }, + state: State{ + chainID("democ"): ChainState{ + ValPowers: &map[validatorID]uint{ + validatorID("alice"): 511, + validatorID("bob"): 495, + validatorID("carol"): 500, + }, + //Check that slashing on the gov-consumer chain does not result in slashing for the representatives or their delegators + RepresentativePowers: &map[validatorID]uint{ + validatorID("alice"): 100500000, + validatorID("bob"): 40000000, + }, + }, + }, + }, } From f8e849fb05e48c12f8881cf450578818644a8b21 Mon Sep 17 00:00:00 2001 From: stana-ethernal Date: Tue, 13 Sep 2022 11:20:07 +0200 Subject: [PATCH 11/13] Clean up and e2e test for democracy distribution --- .../ante/msg_filter_ante.go | 59 ------ .../ante/msg_filter_ante_test.go | 85 -------- app/consumer-democracy/ante_handler.go | 2 +- app/consumer-democracy/app.go | 3 - .../democracy => tests/e2e}/democracy_test.go | 194 +++++------------- testutil/simapp/simapp.go | 2 +- x/ccv/consumer/keeper/distribution.go | 3 - 7 files changed, 55 insertions(+), 293 deletions(-) delete mode 100644 app/consumer-democracy/ante/msg_filter_ante.go delete mode 100644 app/consumer-democracy/ante/msg_filter_ante_test.go rename {x/ccv/democracy => tests/e2e}/democracy_test.go (51%) diff --git a/app/consumer-democracy/ante/msg_filter_ante.go b/app/consumer-democracy/ante/msg_filter_ante.go deleted file mode 100644 index a9b2b2bd78..0000000000 --- a/app/consumer-democracy/ante/msg_filter_ante.go +++ /dev/null @@ -1,59 +0,0 @@ -package ante - -import ( - "fmt" - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var validMsgsCCVDisabled = map[string]struct{}{} - -type ( - // ConsumerKeeper defines the interface required by a consumer module keeper. - ConsumerKeeper interface { - GetProviderChannel(ctx sdk.Context) (string, bool) - } - - // MsgFilterDecorator defines an AnteHandler decorator that enables message - // filtering based on certain criteria. - MsgFilterDecorator struct { - ConsumerKeeper ConsumerKeeper - } -) - -// TODO Ethernal: remove this, it is duplicated (consumerante does the same thing) -func NewMsgFilterDecorator(k ConsumerKeeper) MsgFilterDecorator { - return MsgFilterDecorator{ - ConsumerKeeper: k, - } -} - -func (mfd MsgFilterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - currHeight := ctx.BlockHeight() - - // If the CCV channel has not yet been established, then we must only allow certain - // message types. - if _, ok := mfd.ConsumerKeeper.GetProviderChannel(ctx); !ok { - if !hasValidMsgsPreCCV(tx.GetMsgs()) { - return ctx, fmt.Errorf("tx contains unsupported message types at height %d", currHeight) - } - } - - return next(ctx, tx, simulate) -} - -func hasValidMsgsPreCCV(msgs []sdk.Msg) bool { - for _, msg := range msgs { - msgType := sdk.MsgTypeURL(msg) - - // Only accept IBC messages prior to the CCV channel being established. - // Note, rather than listing out all possible IBC message types, we assume - // all IBC message types have a correct and canonical prefix -- /ibc.* - if !strings.HasPrefix(msgType, "/ibc.") { - return false - } - } - - return true -} diff --git a/app/consumer-democracy/ante/msg_filter_ante_test.go b/app/consumer-democracy/ante/msg_filter_ante_test.go deleted file mode 100644 index 6fb1f08955..0000000000 --- a/app/consumer-democracy/ante/msg_filter_ante_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package ante_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - appconsumer "github.com/cosmos/interchain-security/app/consumer" - "github.com/cosmos/interchain-security/app/consumer/ante" - "github.com/stretchr/testify/require" - "github.com/tendermint/spm/cosmoscmd" -) - -type consumerKeeper struct { - channelExists bool -} - -func (k consumerKeeper) GetProviderChannel(_ sdk.Context) (string, bool) { - return "", k.channelExists -} - -func noOpAnteDecorator() sdk.AnteHandler { - return func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { - return ctx, nil - } -} - -func TestMsgFilterDecorator(t *testing.T) { - txCfg := cosmoscmd.MakeEncodingConfig(appconsumer.ModuleBasics).TxConfig - - testCases := []struct { - name string - ctx sdk.Context - consumerKeeper ante.ConsumerKeeper - msgs []sdk.Msg - expectErr bool - }{ - { - name: "valid tx pre-CCV", - ctx: sdk.Context{}, - consumerKeeper: consumerKeeper{channelExists: false}, - msgs: []sdk.Msg{ - &ibcclienttypes.MsgUpdateClient{}, - }, - expectErr: false, - }, - { - name: "invalid tx pre-CCV", - ctx: sdk.Context{}, - consumerKeeper: consumerKeeper{channelExists: false}, - msgs: []sdk.Msg{ - &banktypes.MsgSend{}, - }, - expectErr: true, - }, - { - name: "valid tx post-CCV", - ctx: sdk.Context{}, - consumerKeeper: consumerKeeper{channelExists: true}, - msgs: []sdk.Msg{ - &banktypes.MsgSend{}, - }, - expectErr: false, - }, - } - - for _, tc := range testCases { - tc := tc - - t.Run(tc.name, func(t *testing.T) { - handler := ante.NewMsgFilterDecorator(tc.consumerKeeper) - - txBuilder := txCfg.NewTxBuilder() - require.NoError(t, txBuilder.SetMsgs(tc.msgs...)) - - _, err := handler.AnteHandle(tc.ctx, txBuilder.GetTx(), false, noOpAnteDecorator()) - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go index 557a245756..5e282ae06a 100644 --- a/app/consumer-democracy/ante_handler.go +++ b/app/consumer-democracy/ante_handler.go @@ -39,7 +39,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), consumerante.NewMsgFilterDecorator(options.ConsumerKeeper), - // TODO Ethernal add handler to disable slashing and evidence messages (issue 115) + consumerante.NewDisabledModulesDecorator("/cosmos.evidence", "/cosmos.slashing"), ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 6aacfffa02..1492244ce0 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -80,19 +80,16 @@ import ( tmos "github.com/tendermint/tendermint/libs/os" dbm "github.com/tendermint/tm-db" - // add ccv distribution CALUM distr "github.com/cosmos/cosmos-sdk/x/distribution" ccvdistrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" ccvdistrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" ccvdistrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" ccvdistr "github.com/cosmos/interchain-security/x/ccv/democracy/distribution" - // add ccv staking BILY ccvstakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ccvstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ccvstaking "github.com/cosmos/interchain-security/x/ccv/democracy/staking" - // add gov Billy ccvgov "github.com/cosmos/cosmos-sdk/x/gov" ccvgovkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" ccvgovtypes "github.com/cosmos/cosmos-sdk/x/gov/types" diff --git a/x/ccv/democracy/democracy_test.go b/tests/e2e/democracy_test.go similarity index 51% rename from x/ccv/democracy/democracy_test.go rename to tests/e2e/democracy_test.go index c3dafeda8f..18d5d86349 100644 --- a/x/ccv/democracy/democracy_test.go +++ b/tests/e2e/democracy_test.go @@ -1,14 +1,10 @@ -package democracy_test +package e2e_test -// TODO Ethernal: move to e2e folder, remove commented code import ( "bytes" - "fmt" - "regexp" "testing" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -18,6 +14,7 @@ import ( appConsumer "github.com/cosmos/interchain-security/app/consumer-democracy" appProvider "github.com/cosmos/interchain-security/app/provider" "github.com/cosmos/interchain-security/testutil/simapp" + consumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" "github.com/cosmos/interchain-security/x/ccv/types" "github.com/cosmos/interchain-security/x/ccv/utils" @@ -27,6 +24,8 @@ import ( "github.com/stretchr/testify/suite" ) +var consumerFraction, _ = sdk.NewDecFromStr(consumerkeeper.ConsumerRedistributeFrac) + type ConsumerDemocracyTestSuite struct { suite.Suite @@ -38,8 +37,6 @@ type ConsumerDemocracyTestSuite struct { path *ibctesting.Path transferPath *ibctesting.Path - - // providerDistrIndex int } func (s *ConsumerDemocracyTestSuite) SetupTest() { @@ -184,141 +181,71 @@ func TestConsumerDemocracyTestSuite(t *testing.T) { suite.Run(t, new(ConsumerDemocracyTestSuite)) } -func (s *ConsumerDemocracyTestSuite) checkBalancesOverNextBlock() { +func (s *ConsumerDemocracyTestSuite) TestDemocracyRewarsDistribution() { + s.consumerChain.NextBlock() stakingKeeper := s.consumerChain.App.(*appConsumer.App).StakingKeeper authKeeper := s.consumerChain.App.(*appConsumer.App).AccountKeeper distrKeeper := s.consumerChain.App.(*appConsumer.App).DistrKeeper bankKeeper := s.consumerChain.App.(*appConsumer.App).BankKeeper - - distrAccount := distrKeeper.GetDistributionAccount(s.consumerCtx()) - - fmt.Printf("feeAccountName is %s\n", authtypes.FeeCollectorName) - fmt.Printf("consumerRedistributeAccount is %s\n", consumertypes.ConsumerRedistributeName) - fmt.Printf("providerRedistributeAccount is %s\n", consumertypes.ConsumerToSendToProviderName) - feeCollectorAccount := authKeeper.GetModuleAccount(s.consumerCtx(), authtypes.FeeCollectorName) - consumerRedistributeAccount := authKeeper.GetModuleAccount(s.consumerCtx(), consumertypes.ConsumerRedistributeName) - providerRedistributeAccount := authKeeper.GetModuleAccount(s.consumerCtx(), consumertypes.ConsumerToSendToProviderName) - bondDenom := stakingKeeper.BondDenom(s.consumerCtx()) - distrAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - feeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), feeCollectorAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - cosumerFeeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - providerFeeAccountBalance0 := bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - - fmt.Println("distrAccountBalance0", formatCommas(distrAccountBalance0)) - fmt.Println("feeAccountBalance0", formatCommas(feeAccountBalance0)) - fmt.Println("cosumerFeeAccountBalance0", formatCommas(cosumerFeeAccountBalance0)) - fmt.Println("providerFeeAccountBalance0", formatCommas(providerFeeAccountBalance0)) - - s.consumerChain.NextBlock() - - distrAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - feeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), feeCollectorAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - cosumerFeeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - providerFeeAccountBalance1 := bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount.QuoRaw(1000000) - - fmt.Println("distrAccountBalance1", formatCommas(distrAccountBalance1)) - fmt.Println("feeAccountBalance1", formatCommas(feeAccountBalance1)) - fmt.Println("cosumerFeeAccountBalance1", formatCommas(cosumerFeeAccountBalance1)) - fmt.Println("providerFeeAccountBalance1", formatCommas(providerFeeAccountBalance1)) - - distrDifference := distrAccountBalance1.Sub(distrAccountBalance0) - fmt.Println("distrDifference", formatCommas(distrDifference)) - - providerDifference := providerFeeAccountBalance1.Sub(providerFeeAccountBalance0) - fmt.Println("providerDifference", formatCommas(providerDifference)) - - ratio := providerFeeAccountBalance1.ToDec().QuoInt(distrAccountBalance1) - fmt.Println("ratio between distr and provider pool", ratio) -} - -func (s *ConsumerDemocracyTestSuite) TestPacketRoundtrip() { - s.SetupCCVChannel() - - s.checkBalancesOverNextBlock() - s.checkBalancesOverNextBlock() - s.checkBalancesOverNextBlock() - s.checkBalancesOverNextBlock() - - // s.consumerChain.NextBlock() - - // feeAccountBalance2 := bankKeeper.GetBalance(s.consumerCtx(), distrAccount.GetAddress(), bondDenom) - // cosumerFeeAccountBalance2 := bankKeeper.GetBalance(s.consumerCtx(), consumerRedistributeAccount.GetAddress(), bondDenom) + currentRepresentativesRewards := map[string]sdk.Dec{} + nextRepresentativesRewards := map[string]sdk.Dec{} + representativesTokens := map[string]sdk.Int{} - // fmt.Println("feeAccountBalance2", feeAccountBalance2) - // fmt.Println("cosumerFeeAccountBalance2", cosumerFeeAccountBalance2) - - // feeAccountBalanceDifference = feeAccountBalance2.Sub(feeAccountBalance1) - // fmt.Println("feeAccountBalanceDifference", feeAccountBalanceDifference) - - // // providerCtx := s.providerChain.GetContext() - // providerStakingKeeper := s.providerChain.App.(*appProvider.App).StakingKeeper - - // origTime := s.providerCtx().BlockTime() - // bondAmt := sdk.NewInt(1000000) - - // delAddr := s.providerChain.SenderAccount.GetAddress() - - // // Choose a validator, and get its address and data structure into the correct types - // tmValidator := s.providerChain.Vals.Validators[0] - // valAddr, err := sdk.ValAddressFromHex(tmValidator.Address.String()) - // s.Require().NoError(err) - // validator, found := providerStakingKeeper.GetValidator(s.providerCtx(), valAddr) - // s.Require().True(found) - - // // Bond some tokens on provider to change validator powers - // _, err = providerStakingKeeper.Delegate(s.providerCtx(), delAddr, bondAmt, stakingtypes.Unbonded, stakingtypes.Validator(validator), true) - // s.Require().NoError(err) - - // // Save valset update ID to reconstruct packet - // valUpdateID := s.providerChain.App.(*appProvider.App).ProviderKeeper.GetValidatorSetUpdateId(s.providerCtx()) - - // // Send CCV packet to consumer - // s.providerChain.App.EndBlock(abci.RequestEndBlock{}) - - // // Get validator update created in Endblock to use in reconstructing packet - // valUpdates := providerStakingKeeper.GetValidatorUpdates(s.providerCtx()) - - // // commit block on provider chain and update consumer chain's client - // oldBlockTime := s.providerCtx().BlockTime() - // s.coordinator.CommitBlock(s.providerChain) - // err = s.path.EndpointA.UpdateClient() - // s.Require().NoError(err) + for _, representative := range stakingKeeper.GetAllValidators(s.consumerCtx()) { + currentRepresentativesRewards[representative.OperatorAddress] = sdk.NewDec(0) + nextRepresentativesRewards[representative.OperatorAddress] = sdk.NewDec(0) + representativesTokens[representative.OperatorAddress] = representative.GetTokens() + } - // // Reconstruct packet - // packetData := types.NewValidatorSetChangePacketData(valUpdates, valUpdateID, nil) - // timeout := uint64(ccv.GetTimeoutTimestamp(oldBlockTime).UnixNano()) - // packet := channeltypes.NewPacket(packetData.GetBytes(), 1, providertypes.PortID, s.path.EndpointB.ChannelID, - // consumertypes.PortID, s.path.EndpointA.ChannelID, clienttypes.Height{}, timeout) + distrModuleAccount := distrKeeper.GetDistributionAccount(s.consumerCtx()) + providerRedistributeAccount := authKeeper.GetModuleAccount(s.consumerCtx(), consumertypes.ConsumerToSendToProviderName) + //balance of consumer redistribute address will always be 0 when checked between 2 NextBlock() calls + + currentDistrModuleAccountBalance := sdk.NewDecFromInt(bankKeeper.GetBalance(s.consumerCtx(), distrModuleAccount.GetAddress(), bondDenom).Amount) + currentProviderFeeAccountBalance := sdk.NewDecFromInt(bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount) + currentCommunityPoolBalance := distrKeeper.GetFeePoolCommunityCoins(s.consumerCtx()).AmountOf(bondDenom) + for key := range currentRepresentativesRewards { + representativeAddr, _ := sdk.ValAddressFromBech32(key) + representativeReward := distrKeeper.GetValidatorOutstandingRewards(s.consumerCtx(), representativeAddr).Rewards.AmountOf(bondDenom) + currentRepresentativesRewards[key] = representativeReward + } - // // Receive CCV packet on consumer chain - // err = s.path.EndpointA.RecvPacket(packet) - // s.Require().NoError(err) + s.consumerChain.NextBlock() - // // - End provider unbonding period - // // providerCtx = providerCtx.WithBlockTime(origTime.Add(providerStakingKeeper.UnbondingTime(s.providerCtx())).Add(3 * time.Hour)) - // s.providerChain.App.EndBlock(abci.RequestEndBlock{}) + nextDistrModuleAccountBalance := sdk.NewDecFromInt(bankKeeper.GetBalance(s.consumerCtx(), distrModuleAccount.GetAddress(), bondDenom).Amount) + nextProviderFeeAccountBalance := sdk.NewDecFromInt(bankKeeper.GetBalance(s.consumerCtx(), providerRedistributeAccount.GetAddress(), bondDenom).Amount) + nextCommunityPoolBalance := distrKeeper.GetFeePoolCommunityCoins(s.consumerCtx()).AmountOf(bondDenom) + for key := range nextRepresentativesRewards { + representativeAddr, _ := sdk.ValAddressFromBech32(key) + representativeReward := distrKeeper.GetValidatorOutstandingRewards(s.consumerCtx(), representativeAddr).Rewards.AmountOf(bondDenom) + nextRepresentativesRewards[key] = representativeReward + } - // // - End consumer unbonding period - // unbondingPeriod, found := s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.GetUnbondingTime(s.consumerCtx()) - // s.Require().True(found) - // consumerCtx := s.consumerCtx().WithBlockTime(origTime.Add(unbondingPeriod).Add(3 * time.Hour)) - // // TODO: why doesn't this work: s.consumerChain.App.EndBlock(abci.RequestEndBlock{}) - // err = s.consumerChain.App.(*appConsumer.App).ConsumerKeeper.UnbondMaturePackets(consumerCtx) - // s.Require().NoError(err) + distrModuleDifference := nextDistrModuleAccountBalance.Sub(currentDistrModuleAccountBalance) + providerDifference := nextProviderFeeAccountBalance.Sub(currentProviderFeeAccountBalance) + communityPoolDifference := nextCommunityPoolBalance.Sub(currentCommunityPoolBalance) + representativeDifference := map[string]sdk.Dec{} + consumerRedistributeDifference := communityPoolDifference - // // commit consumer chain and update provider chain client - // s.coordinator.CommitBlock(s.consumerChain) + for key, currentReward := range currentRepresentativesRewards { + representativeDifference[key] = nextRepresentativesRewards[key].Sub(currentReward) + consumerRedistributeDifference = consumerRedistributeDifference.Add(representativeDifference[key]) + } - // err = s.path.EndpointB.UpdateClient() - // s.Require().NoError(err) + s.Require().Equal(distrModuleDifference, consumerRedistributeDifference) + s.Require().Equal(communityPoolDifference.Quo(consumerRedistributeDifference), distrKeeper.GetCommunityTax(s.consumerCtx())) + s.Require().InEpsilon(distrModuleDifference.Quo(providerDifference.Add(distrModuleDifference)).MustFloat64(), consumerFraction.MustFloat64(), float64(0.0001)) + s.Require().InEpsilon(providerDifference.Quo(providerDifference.Add(distrModuleDifference)).MustFloat64(), sdk.NewDec(1).Sub(consumerFraction).MustFloat64(), float64(0.0001)) - // ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + totalRepresentativePower := stakingKeeper.GetValidatorSet().TotalBondedTokens(s.consumerCtx()) - // err = s.path.EndpointB.AcknowledgePacket(packet, ack.Acknowledgement()) - // s.Require().NoError(err) + for key, representativeTokens := range representativesTokens { + powerFraction := sdk.NewDecFromInt(representativeTokens).QuoTruncate(sdk.NewDecFromInt(totalRepresentativePower)) + s.Require().Equal(powerFraction, representativeDifference[key].Quo(consumerRedistributeDifference.Sub(communityPoolDifference))) + } } func (s *ConsumerDemocracyTestSuite) providerCtx() sdk.Context { @@ -328,18 +255,3 @@ func (s *ConsumerDemocracyTestSuite) providerCtx() sdk.Context { func (s *ConsumerDemocracyTestSuite) consumerCtx() sdk.Context { return s.consumerChain.GetContext() } - -// func (s *ConsumerDemocracyTestSuite) providerBondDenom() string { -// return s.providerChain.App.(*appProvider.App).StakingKeeper.BondDenom(s.providerCtx()) -// } - -func formatCommas(numm sdk.Int) string { - num := numm.Int64() - str := fmt.Sprintf("%d", num) - re := regexp.MustCompile(`(\\d+)(\\d{3})`) - for n := ""; n != str; { - n = str - str = re.ReplaceAllString(str, "$1,$2") - } - return str -} diff --git a/testutil/simapp/simapp.go b/testutil/simapp/simapp.go index 74e5c0bfd9..cd76500be6 100644 --- a/testutil/simapp/simapp.go +++ b/testutil/simapp/simapp.go @@ -65,7 +65,7 @@ func NewProviderConsumerCoordinator(t *testing.T) (*ibctesting.Coordinator, *ibc return coordinator, providerChain, consumerChain } -// NewCoordinator initializes Coordinator with 0 TestChains +// NewCoordinator initializes Coordinator with provider and democracy consumer TestChains func NewProviderConsumerDemocracyCoordinator(t *testing.T) (*ibctesting.Coordinator, *ibctesting.TestChain, *ibctesting.TestChain) { coordinator := NewBasicCoordinator(t) chainID := ibctesting.GetChainID(1) diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index befd832c08..1e17ed178d 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -1,7 +1,6 @@ package keeper import ( - "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -42,11 +41,9 @@ func (k Keeper) DistributeToProviderValidatorSet(ctx sdk.Context) error { decFPTokens := sdk.NewDecCoinsFromCoins(fpTokens...) // NOTE the truncated decimal remainder will be sent to the provider fee pool consRedistrTokens, _ := decFPTokens.MulDec(frac).TruncateDecimal() - fmt.Println("consRedistrTokens", consRedistrTokens) err = k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ConsumerRedistributeName, consRedistrTokens) if err != nil { - fmt.Println("error sending to ConsumerRedistributeName", err) return err } From fd2ff35655fbbf14596fa7d3b197850c529b8397 Mon Sep 17 00:00:00 2001 From: stana-ethernal Date: Wed, 21 Sep 2022 10:08:08 +0200 Subject: [PATCH 12/13] gov-distribution module - cr fix --- tests/e2e/democracy_test.go | 5 ++++ x/ccv/democracy/distribution/doc.go | 2 +- x/ccv/democracy/distribution/module.go | 33 ++++++++++---------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/e2e/democracy_test.go b/tests/e2e/democracy_test.go index 18d5d86349..f399e04627 100644 --- a/tests/e2e/democracy_test.go +++ b/tests/e2e/democracy_test.go @@ -235,13 +235,18 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyRewarsDistribution() { consumerRedistributeDifference = consumerRedistributeDifference.Add(representativeDifference[key]) } + //confirm that the total amount given to the community pool plus all representatives is equal to the total amount taken out of distribution s.Require().Equal(distrModuleDifference, consumerRedistributeDifference) + //confirm that the percentage given to the community pool is equal to the configured community tax percentage. s.Require().Equal(communityPoolDifference.Quo(consumerRedistributeDifference), distrKeeper.GetCommunityTax(s.consumerCtx())) + //check that the fraction actually kept by the consumer is the correct fraction. using InEpsilon because the math code uses truncations s.Require().InEpsilon(distrModuleDifference.Quo(providerDifference.Add(distrModuleDifference)).MustFloat64(), consumerFraction.MustFloat64(), float64(0.0001)) + //check that the fraction actually kept by the provider is the correct fraction. using InEpsilon because the math code uses truncations s.Require().InEpsilon(providerDifference.Quo(providerDifference.Add(distrModuleDifference)).MustFloat64(), sdk.NewDec(1).Sub(consumerFraction).MustFloat64(), float64(0.0001)) totalRepresentativePower := stakingKeeper.GetValidatorSet().TotalBondedTokens(s.consumerCtx()) + //check that each representative has gotten the correct amount of rewards for key, representativeTokens := range representativesTokens { powerFraction := sdk.NewDecFromInt(representativeTokens).QuoTruncate(sdk.NewDecFromInt(totalRepresentativePower)) s.Require().Equal(powerFraction, representativeDifference[key].Quo(consumerRedistributeDifference.Sub(communityPoolDifference))) diff --git a/x/ccv/democracy/distribution/doc.go b/x/ccv/democracy/distribution/doc.go index fd51ea0589..2905f74cd4 100644 --- a/x/ccv/democracy/distribution/doc.go +++ b/x/ccv/democracy/distribution/doc.go @@ -1,5 +1,5 @@ /* -Package staking defines a "wrapper" module around the Cosmos SDK's native +Package distribution defines a "wrapper" module around the Cosmos SDK's native x/distribution module. In other words, it provides the exact same functionality as the native module in that it simply embeds the native module. diff --git a/x/ccv/democracy/distribution/module.go b/x/ccv/democracy/distribution/module.go index 6591fc95f0..4c535aad44 100644 --- a/x/ccv/democracy/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -24,7 +24,7 @@ var ( _ module.AppModuleSimulation = AppModule{} ) -// AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. +// AppModule embeds the Cosmos SDK's x/distribution AppModuleBasic. type AppModuleBasic struct { distr.AppModuleBasic } @@ -69,22 +69,16 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { if ctx.BlockHeight() > 1 { am.AllocateTokens(ctx) } - - // record the proposer for when we payout on the next block - consAddr := sdk.ConsAddress(req.Header.ProposerAddress) - am.keeper.SetPreviousProposerConsAddr(ctx, consAddr) } // AllocateTokens handles distribution of the collected fees -// bondedVotes is a list of (validator address, validator voted on last block flag) for all -// validators in the bonded set. func (am AppModule) AllocateTokens( ctx sdk.Context, ) { // fetch and clear the collected fees for distribution, since this is // called in BeginBlock, collected fees will be from the previous block - // (and distributed to the previous proposer) + // (and distributed to the current representatives) feeCollector := am.accountKeeper.GetModuleAccount(ctx, consumertypes.ConsumerRedistributeName) feesCollectedInt := am.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) @@ -99,27 +93,25 @@ func (am AppModule) AllocateTokens( // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 feePool := am.keeper.GetFeePool(ctx) vs := am.stakingKeeper.GetValidatorSet() - totalPower := vs.TotalBondedTokens(ctx) - if totalPower.IsZero() { + totalBondedTokens := vs.TotalBondedTokens(ctx) + if totalBondedTokens.IsZero() { feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) am.keeper.SetFeePool(ctx, feePool) return } - // calculate fraction allocated to validators + // calculate the fraction allocated to representatives by subtracting the community tax. + // e.g. if community tax is 0.02, representatives fraction will be 0.98 (2% goes to the community pool and the rest to the representatives) remaining := feesCollected communityTax := am.keeper.GetCommunityTax(ctx) - voteMultiplier := sdk.OneDec().Sub(communityTax) - - // allocate tokens proportionally to voting power - // TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376 + representativesFraction := sdk.OneDec().Sub(communityTax) + // allocate tokens proportionally to representatives voting power vs.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool { - - // TODO consider microslashing for missing votes. - // ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701 - powerFraction := sdk.NewDecFromInt(validator.GetTokens()).QuoTruncate(sdk.NewDecFromInt(totalPower)) - reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction) + //we get this validator's percentage of the total power by dividing their tokens by the total bonded tokens + powerFraction := sdk.NewDecFromInt(validator.GetTokens()).QuoTruncate(sdk.NewDecFromInt(totalBondedTokens)) + //we truncate here again, which means that the reward will be slightly lower than it should be + reward := feesCollected.MulDecTruncate(representativesFraction).MulDecTruncate(powerFraction) am.keeper.AllocateTokensToValidator(ctx, validator, reward) remaining = remaining.Sub(reward) @@ -127,6 +119,7 @@ func (am AppModule) AllocateTokens( }) // allocate community funding + //due to the 3 truncations above, remaining sent to the community pool will be slightly more than it should be. This is OK feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) am.keeper.SetFeePool(ctx, feePool) } From ca56d87190a2166a5e9e023ba8958487d9b3ba3d Mon Sep 17 00:00:00 2001 From: Jehan Tremback Date: Wed, 21 Sep 2022 13:35:49 -0700 Subject: [PATCH 13/13] fix small merge issue --- tests/integration/main.go | 6 +++--- tests/integration/steps_democracy.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/main.go b/tests/integration/main.go index 4de7ff027d..fada60b41b 100644 --- a/tests/integration/main.go +++ b/tests/integration/main.go @@ -11,7 +11,7 @@ import ( "github.com/kylelemons/godebug/pretty" ) -var verbose = true +var verbose = false func main() { fmt.Println("============================================ start happy path tests ============================================") @@ -27,7 +27,7 @@ func main() { fmt.Printf("happy path tests successful - time elapsed %v\n", time.Since(start)) - fmt.Println("============================================ start democracy path tests ============================================") + fmt.Println("============================================ start democracy tests ============================================") start = time.Now() tr.startDocker() @@ -35,7 +35,7 @@ func main() { tr.runStep(step, verbose) } - fmt.Printf("democracy path tests successful - time elapsed %v\n", time.Since(start)) + fmt.Printf("democracy tests successful - time elapsed %v\n", time.Since(start)) } func (tr TestRun) runStep(step Step, verbose bool) { diff --git a/tests/integration/steps_democracy.go b/tests/integration/steps_democracy.go index f442ab377b..537411a450 100644 --- a/tests/integration/steps_democracy.go +++ b/tests/integration/steps_democracy.go @@ -1,7 +1,7 @@ package main import ( - clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" ) var democracySteps = []Step{