Skip to content

Commit

Permalink
PSS reward distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
sainoe committed Feb 8, 2024
1 parent 72a1407 commit 2a0fa7a
Show file tree
Hide file tree
Showing 13 changed files with 1,070 additions and 182 deletions.
14 changes: 9 additions & 5 deletions app/provider/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import (

appencoding "github.com/cosmos/interchain-security/v4/app/encoding"
testutil "github.com/cosmos/interchain-security/v4/testutil/integration"
"github.com/cosmos/interchain-security/v4/x/ccv/provider"
ibcprovider "github.com/cosmos/interchain-security/v4/x/ccv/provider"
ibcproviderclient "github.com/cosmos/interchain-security/v4/x/ccv/provider/client"
ibcproviderkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper"
Expand Down Expand Up @@ -470,12 +471,15 @@ func New(
app.BankKeeper,
scopedTransferKeeper,
)
transferModule := transfer.NewAppModule(app.TransferKeeper)
ibcmodule := transfer.NewIBCModule(app.TransferKeeper)

// Add a IBC middleware callback to track the consumer rewards
var transferStack porttypes.IBCModule
transferStack = transfer.NewIBCModule(app.TransferKeeper)
transferStack = provider.NewIBCMiddleware(transferStack, app.ProviderKeeper)

// create static IBC router, add transfer route, then set and seal it
ibcRouter := porttypes.NewRouter()
ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcmodule)
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
ibcRouter.AddRoute(providertypes.ModuleName, providerModule)
app.IBCKeeper.SetRouter(ibcRouter)

Expand Down Expand Up @@ -514,7 +518,7 @@ func New(
evidence.NewAppModule(app.EvidenceKeeper),
ibc.NewAppModule(app.IBCKeeper),
params.NewAppModule(app.ParamsKeeper),
transferModule,
transfer.NewAppModule(app.TransferKeeper),
providerModule,
)

Expand Down Expand Up @@ -610,7 +614,7 @@ func New(
params.NewAppModule(app.ParamsKeeper),
evidence.NewAppModule(app.EvidenceKeeper),
ibc.NewAppModule(app.IBCKeeper),
transferModule,
transfer.NewAppModule(app.TransferKeeper),
)

app.sm.RegisterStoreDecoders()
Expand Down
11 changes: 11 additions & 0 deletions proto/interchain_security/ccv/provider/v1/provider.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import "ibc/lightclients/tendermint/v1/tendermint.proto";
import "tendermint/crypto/keys.proto";
import "cosmos/evidence/v1beta1/evidence.proto";
import "cosmos/base/v1beta1/coin.proto";
import "amino/amino.proto";


//
// Note any type defined in this file is ONLY used internally to the provider CCV module.
Expand Down Expand Up @@ -300,3 +302,12 @@ message ConsumerAddrsToPrune {
uint64 vsc_id = 2;
AddressList consumer_addrs = 3;
}

// ConsumerRewardsAllocation is used to serialize the allocation of consumer chain rewards
message ConsumerRewardsAllocation {
repeated cosmos.base.v1beta1.DecCoin rewards = 1 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"
];
}
147 changes: 147 additions & 0 deletions tests/integration/distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ package integration
import (
"strings"

"cosmossdk.io/math"
"github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
icstestingutils "github.com/cosmos/interchain-security/v4/testutil/integration"
consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper"
consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"
providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper"
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
ccv "github.com/cosmos/interchain-security/v4/x/ccv/types"
)
Expand Down Expand Up @@ -110,6 +116,9 @@ func (s *CCVTestSuite) TestRewardsDistribution() {
s.Require().Equal(0, len(rewardCoins))

// check that the fee pool has the expected amount of coins
// Note that all rewards are allocated to the community pool since
// BeginBlock is called without the validators' votes in ibctesting.
// See NextBlock() in https://github.com/cosmos/ibc-go/blob/release/v7.3.x/testing/chain.go#L281
communityCoins := s.providerApp.GetTestDistributionKeeper().GetFeePoolCommunityCoins(s.providerCtx())
s.Require().True(communityCoins[ibcCoinIndex].Amount.Equal(sdk.NewDecCoinFromCoin(providerExpectedRewards[0]).Amount))
}
Expand Down Expand Up @@ -454,6 +463,144 @@ func (s *CCVTestSuite) TestSendRewardsToProvider() {
}
}

// TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback
func (s *CCVTestSuite) TestIBCTransferMiddleware() {

var (
data ibctransfertypes.FungibleTokenPacketData
packet channeltypes.Packet
)

testCases := []struct {
name string
setup func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper)
expError bool
rewardsAllocated int
tokenTransfers int
}{
{
"invalid IBC packet",
func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) {
packet = channeltypes.Packet{}
},
true,
0,
0,
},
{
"invalid fungible token packet data",
func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) {
packet.Data = nil
},
true,
0,
0,
},
{
"successful token transfer to empty pool",
func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) {
bankKeeper.SendCoinsFromAccountToModule(
ctx,
s.providerChain.SenderAccount.GetAddress(),
providertypes.ConsumerRewardsPool,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100_000))),
)

keeper.SetConsumerRewardsAllocation(
ctx,
s.consumerChain.ChainID,
providertypes.ConsumerRewardsAllocation{
Rewards: sdk.NewDecCoins(sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(100_000))),
},
)
},
false,
1,
1,
},
{
"successful token transfer to filled pool",
func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) {
// fill consumer reward pool
bankKeeper.SendCoinsFromAccountToModule(
ctx,
s.providerChain.SenderAccount.GetAddress(),
providertypes.ConsumerRewardsPool,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100_000))),
)
// update consumer allocation
keeper.SetConsumerRewardsAllocation(
ctx,
s.consumerChain.ChainID,
providertypes.ConsumerRewardsAllocation{
Rewards: sdk.NewDecCoins(sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(100_000))),
},
)
},
false,
1,
1,
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
s.SetupCCVChannel(s.path)
s.SetupTransferChannel()

providerkeeper := s.providerApp.GetProviderKeeper()
amount := sdk.NewInt(100)

data = types.NewFungibleTokenPacketData( // can be explicitly changed in setup
sdk.DefaultBondDenom,
amount.String(),
authtypes.NewModuleAddress(consumertypes.ConsumerToSendToProviderName).String(),
providerkeeper.GetConsumerRewardsPoolAddressStr(s.providerCtx()),
"",
)

packet = channeltypes.NewPacket( // can be explicitly changed in setup
data.GetBytes(),
uint64(1),
s.transferPath.EndpointA.ChannelConfig.PortID,
s.transferPath.EndpointA.ChannelID,
s.transferPath.EndpointB.ChannelConfig.PortID,
s.transferPath.EndpointB.ChannelID,
clienttypes.NewHeight(1, 100),
0,
)

tc.setup(s.providerCtx(), &providerkeeper, s.providerApp.GetTestBankKeeper())

cbs, ok := s.providerChain.App.GetIBCKeeper().Router.GetRoute(ibctransfertypes.ModuleName)
s.Require().True(ok)

prevConsumerPool := providerkeeper.GetConsumerRewardsPool(s.providerCtx())
prevConsumerAlloc := providerkeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID)

ack := cbs.OnRecvPacket(s.providerCtx(), packet, sdk.AccAddress{})
if !tc.expError {
s.Require().True(ack.Success())

// verify consumer rewards pool and allocation are updated
pool := providerkeeper.GetConsumerRewardsPool(s.providerCtx()).Sub(prevConsumerPool...)
s.Require().True(amount.Equal(pool[0].Amount))

alloc := providerkeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID).Rewards.Sub(prevConsumerAlloc.Rewards)
s.Require().True(sdk.NewDecCoinsFromCoins(pool...).IsEqual(alloc))
} else {
s.Require().False(ack.Success())
receivedCoins := providerkeeper.GetConsumerRewardsPool(s.providerCtx()).Sub(prevConsumerPool...)
s.Require().True(receivedCoins.Empty())

alloc := providerkeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID).Rewards.Sub(prevConsumerAlloc.Rewards)
s.Require().True(alloc.Empty())
}
})
}
}

// getEscrowBalance gets the current balances in the escrow account holding the transferred tokens to the provider
func (s *CCVTestSuite) getEscrowBalance() sdk.Coins {
consumerBankKeeper := s.consumerApp.GetTestBankKeeper()
Expand Down
4 changes: 4 additions & 0 deletions testutil/integration/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,7 @@ func TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations(t *testing
func TestSlashRetries(t *testing.T) {
runCCVTestByName(t, "TestSlashRetries")
}

func TestIBCTransferMiddleware(t *testing.T) {
runCCVTestByName(t, "TestIBCTransferMiddleware")
}
Loading

0 comments on commit 2a0fa7a

Please sign in to comment.