Skip to content

Commit

Permalink
Only do one massive send for all gauge distribution (#510)
Browse files Browse the repository at this point in the history
* Only do one massive send for all gauge distribution

* Fix all the tests
  • Loading branch information
ValarDragon committed Oct 13, 2021
1 parent 268bcbc commit 58f5ea3
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 100 deletions.
12 changes: 7 additions & 5 deletions x/incentives/keeper/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/osmosis-labs/osmosis/app"
"github.com/osmosis-labs/osmosis/x/incentives/types"
lockuptypes "github.com/osmosis-labs/osmosis/x/lockup/types"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
Expand Down Expand Up @@ -151,13 +152,14 @@ func benchmarkDistributionLogic(numAccts, numDenoms, numGauges, numLockups, numD
b.StartTimer()
// distribute coins from gauges to lockup owners
for i := 0; i < numDistrs; i++ {
gauges := []types.Gauge{}
for _, gaugeId := range gaugeIds {
gauge, _ := app.IncentivesKeeper.GetGaugeByID(ctx, gaugeId)
_, err := app.IncentivesKeeper.Distribute(ctx, *gauge)
if err != nil {
fmt.Printf("Distribute, %v\n", err)
b.FailNow()
}
gauges = append(gauges, *gauge)
}
_, err := app.IncentivesKeeper.Distribute(ctx, gauges)
if err != nil {
b.FailNow()
}
}
}
Expand Down
94 changes: 30 additions & 64 deletions x/incentives/keeper/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,55 +279,6 @@ func (k Keeper) FilteredLocksDistributionEst(ctx sdk.Context, gauge types.Gauge,
return gauge, filteredDistrCoins, nil
}

// Distribute coins from gauge according to its conditions
func (k Keeper) debugDistribute(ctx sdk.Context, gauge types.Gauge) (sdk.Coins, error) {
totalDistrCoins := sdk.NewCoins()
locks := k.GetLocksToDistribution(ctx, gauge.DistributeTo)
lockSum := lockuptypes.SumLocksByDenom(locks, gauge.DistributeTo.Denom)

if lockSum.IsZero() {
return nil, nil
}

remainCoins := gauge.Coins.Sub(gauge.DistributedCoins)
remainEpochs := uint64(1)
if !gauge.IsPerpetual { // set remain epochs when it's not perpetual gauge
remainEpochs = gauge.NumEpochsPaidOver - gauge.FilledEpochs
}
for _, lock := range locks {
distrCoins := sdk.Coins{}
for _, coin := range remainCoins {
// distribution amount = gauge_size * denom_lock_amount / (total_denom_lock_amount * remain_epochs)
denomLockAmt := lock.Coins.AmountOfNoDenomValidation(gauge.DistributeTo.Denom)
amt := coin.Amount.Mul(denomLockAmt).Quo(lockSum.Mul(sdk.NewInt(int64(remainEpochs))))
if amt.IsPositive() {
distrCoins = distrCoins.Add(sdk.NewCoin(coin.Denom, amt))
}
}
distrCoins = distrCoins.Sort()
if distrCoins.Empty() {
continue
}
owner, err := sdk.AccAddressFromBech32(lock.Owner)
if err != nil {
return nil, err
}
if err := k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, owner, distrCoins); err != nil {
return nil, err
}

totalDistrCoins = totalDistrCoins.Add(distrCoins...)
}

// increase filled epochs after distribution
gauge.FilledEpochs += 1
gauge.DistributedCoins = gauge.DistributedCoins.Add(totalDistrCoins...)
k.setGauge(ctx, &gauge)

k.hooks.AfterDistribute(ctx, gauge.Id)
return totalDistrCoins, nil
}

// distributionInfo stores all of the information for pent up sends for rewards distributions.
// This enables us to lower the number of events and calls to back
type distributionInfo struct {
Expand Down Expand Up @@ -367,9 +318,9 @@ func (d *distributionInfo) addLockRewards(lock lockuptypes.PeriodLock, rewards s
return nil
}

func (k Keeper) doDistributionSends(ctx sdk.Context, denom string, distrs distributionInfo) error {
func (k Keeper) doDistributionSends(ctx sdk.Context, distrs *distributionInfo) error {
numIDs := len(distrs.idToDecodedAddr)
ctx.Logger().Debug(fmt.Sprintf("Denom %s: Beginning distribution to %d users", denom, numIDs))
ctx.Logger().Debug(fmt.Sprintf("Beginning distribution to %d users", numIDs))
err := k.bk.SendCoinsFromModuleToManyAccounts(
ctx,
types.ModuleName,
Expand All @@ -378,23 +329,24 @@ func (k Keeper) doDistributionSends(ctx sdk.Context, denom string, distrs distri
if err != nil {
return err
}
ctx.Logger().Debug(fmt.Sprintf("Denom %s: Finished sending, now creating liquidity add events", denom))
ctx.Logger().Debug("Finished sending, now creating liquidity add events")
for id := 0; id < numIDs; id++ {
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.TypeEvtDistribution,
sdk.NewAttribute(types.AttributeLockedDenom, denom),
sdk.NewAttribute(types.AttributeReceiver, distrs.idToBech32Addr[id]),
sdk.NewAttribute(types.AttributeAmount, distrs.idToDistrCoins[id].String()),
),
})
}
ctx.Logger().Debug(fmt.Sprintf("Denom %s: Finished Distributing to %d users", denom, numIDs))
ctx.Logger().Debug(fmt.Sprintf("Finished Distributing to %d users", numIDs))
return nil
}

// Distribute coins from gauge according to its conditions
func (k Keeper) Distribute(ctx sdk.Context, gauge types.Gauge) (sdk.Coins, error) {
// distributeInternal runs the distribution logic for a gauge, and adds the sends to
// the distrInfo computed. It also updates the gauge for the distribution.
func (k Keeper) distributeInternal(
ctx sdk.Context, gauge types.Gauge, distrInfo *distributionInfo) (sdk.Coins, error) {
totalDistrCoins := sdk.NewCoins()
locks := k.GetLocksToDistribution(ctx, gauge.DistributeTo)
lockSum := lockuptypes.SumLocksByDenom(locks, gauge.DistributeTo.Denom)
Expand All @@ -409,8 +361,6 @@ func (k Keeper) Distribute(ctx sdk.Context, gauge types.Gauge) (sdk.Coins, error
remainEpochs = gauge.NumEpochsPaidOver - gauge.FilledEpochs
}

distrInfo := newDistributionInfo()

for _, lock := range locks {
distrCoins := sdk.Coins{}
for _, coin := range remainCoins {
Expand All @@ -434,20 +384,36 @@ func (k Keeper) Distribute(ctx sdk.Context, gauge types.Gauge) (sdk.Coins, error
totalDistrCoins = totalDistrCoins.Add(distrCoins...)
}

err := k.doDistributionSends(ctx, gauge.DistributeTo.Denom, distrInfo)
if err != nil {
return nil, err
}

// increase filled epochs after distribution
gauge.FilledEpochs += 1
gauge.DistributedCoins = gauge.DistributedCoins.Add(totalDistrCoins...)
k.setGauge(ctx, &gauge)

k.hooks.AfterDistribute(ctx, gauge.Id)
return totalDistrCoins, nil
}

// Distribute coins from gauge according to its conditions
func (k Keeper) Distribute(ctx sdk.Context, gauges []types.Gauge) (sdk.Coins, error) {
distrInfo := newDistributionInfo()

totalDistributedCoins := sdk.Coins{}
for _, gauge := range gauges {
gaugeDistributedCoins, err := k.distributeInternal(ctx, gauge, &distrInfo)
if err != nil {
return nil, err
}
totalDistributedCoins = totalDistributedCoins.Add(gaugeDistributedCoins...)
}

err := k.doDistributionSends(ctx, &distrInfo)
if err != nil {
return nil, err
}

k.hooks.AfterEpochDistribution(ctx)
return totalDistributedCoins, nil
}

// GetModuleToDistributeCoins returns sum of to distribute coins for all of the module
func (k Keeper) GetModuleToDistributeCoins(ctx sdk.Context) sdk.Coins {
activeGaugesDistr := k.getToDistributeCoinsFromIterator(ctx, k.ActiveGaugesIterator(ctx))
Expand Down
58 changes: 40 additions & 18 deletions x/incentives/keeper/gauge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,53 @@ import (
// a provided gauge,
func (suite *KeeperTestSuite) TestDistribute() {
twoLockupUser := userLocks{
lockDurations: []time.Duration{time.Second, 2 * time.Second},
lockDurations: []time.Duration{defaultLockDuration, 2 * defaultLockDuration},
lockAmounts: []sdk.Coins{defaultLPTokens, defaultLPTokens},
}
defaultGauge := perpGaugeDesc{
lockDenom: defaultLPDenom,
lockDuration: defaultLockDuration,
rewardAmount: sdk.Coins{sdk.NewInt64Coin(defaultRewardDenom, 3000)},
}
doubleLengthGauge := perpGaugeDesc{
lockDenom: defaultLPDenom,
lockDuration: 2 * defaultLockDuration,
rewardAmount: sdk.Coins{sdk.NewInt64Coin(defaultRewardDenom, 3000)},
}
oneKRewardCoins := sdk.Coins{sdk.NewInt64Coin(defaultRewardDenom, 1000)}
twoKRewardCoins := oneKRewardCoins.Add(oneKRewardCoins...)
twoKRewardCoins := sdk.Coins{sdk.NewInt64Coin(defaultRewardDenom, 2000)}
fiveKRewardCoins := sdk.Coins{sdk.NewInt64Coin(defaultRewardDenom, 5000)}
tests := []struct {
users []userLocks
gauges []perpGaugeDesc
expectedRewards []sdk.Coins
}{
// gauge 1 gives 3k coins. Three locks, all eligible. 1k coins per lock
// so 1k to oneLockupUser, 2k to twoLockupUser
{
users: []userLocks{oneLockupUser, twoLockupUser},
gauges: []perpGaugeDesc{defaultGauge},
expectedRewards: []sdk.Coins{oneKRewardCoins, twoKRewardCoins},
},
// gauge 1 gives 3k coins. Three locks, all eligible.
// gauge 2 gives 3k coins to one lock, in twoLockupUser
// so 1k to oneLockupUser, 5k to twoLockupUser
{
users: []userLocks{oneLockupUser, twoLockupUser},
gauges: []perpGaugeDesc{defaultGauge, doubleLengthGauge},
expectedRewards: []sdk.Coins{oneKRewardCoins, fiveKRewardCoins},
},
}
for _, tc := range tests {
for tcIndex, tc := range tests {
suite.SetupTest()
gauges := suite.SetupGauges(tc.gauges)
addrs := suite.SetupUserLocks(tc.users)
for _, g := range gauges {
suite.app.IncentivesKeeper.Distribute(suite.ctx, g)
}
_, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, gauges)
suite.Require().NoError(err)
// Check expected rewards
for i, addr := range addrs {
bal := suite.app.BankKeeper.GetAllBalances(suite.ctx, addr)
suite.Require().Equal(bal.String(), tc.expectedRewards[i].String())
suite.Require().Equal(tc.expectedRewards[i].String(), bal.String(), "tcnum %d, person %d", tcIndex, i)
}
}
}
Expand All @@ -65,6 +80,8 @@ func (suite *KeeperTestSuite) TestInvalidDurationGaugeCreationValidation() {
suite.Require().NoError(err)
}

// TODO: Make this test table driven, or move whatever it tests into
// the much simpler TestDistribute
func (suite *KeeperTestSuite) TestGetModuleToDistributeCoins() {
// test for module get gauges
suite.SetupTest()
Expand Down Expand Up @@ -100,7 +117,7 @@ func (suite *KeeperTestSuite) TestGetModuleToDistributeCoins() {
suite.Require().NoError(err)

// distribute coins to stakers
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{sdk.NewInt64Coin("stake", 105)})

Expand All @@ -109,6 +126,8 @@ func (suite *KeeperTestSuite) TestGetModuleToDistributeCoins() {
suite.Require().Equal(coins, gaugeCoins.Add(addCoins...).Add(gaugeCoins2...).Sub(distrCoins))
}

// TODO: Make this test table driven, or move whatever it tests into
// the much simpler TestDistribute
func (suite *KeeperTestSuite) TestGetModuleDistributedCoins() {
suite.SetupTest()

Expand All @@ -131,7 +150,7 @@ func (suite *KeeperTestSuite) TestGetModuleDistributedCoins() {
suite.Require().NoError(err)

// distribute coins to stakers
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{sdk.NewInt64Coin("stake", 5)})

Expand All @@ -140,6 +159,9 @@ func (suite *KeeperTestSuite) TestGetModuleDistributedCoins() {
suite.Require().Equal(coins, distrCoins)
}

// TODO: Make this test table driven
// OR if it needs to be script based,
// remove lots of boilerplate so this can actually be followed
func (suite *KeeperTestSuite) TestNonPerpetualGaugeOperations() {
// test for module get gauges
suite.SetupTest()
Expand Down Expand Up @@ -205,8 +227,8 @@ func (suite *KeeperTestSuite) TestNonPerpetualGaugeOperations() {
gauges = suite.app.IncentivesKeeper.GetUpcomingGauges(suite.ctx)
suite.Require().Len(gauges, 0)

// distribute coins to LPers
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
// distribute coins to stakers
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{sdk.NewInt64Coin("stake", 105)})
suite.Require().Equal(
Expand Down Expand Up @@ -312,7 +334,7 @@ func (suite *KeeperTestSuite) TestPerpetualGaugeOperations() {
suite.Require().Len(gauges, 0)

// distribute coins to stakers, since it's perpetual distribute everything on single distribution
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{sdk.NewInt64Coin("stake", 10)})

Expand All @@ -322,9 +344,9 @@ func (suite *KeeperTestSuite) TestPerpetualGaugeOperations() {
// distributing twice without adding more for perpetual gauge
gauge, err = suite.app.IncentivesKeeper.GetGaugeByID(suite.ctx, gaugeID)
suite.Require().NoError(err)
distrCoins, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{})
suite.Require().True(distrCoins.Empty())

suite.Require().Equal(coins.String(),
suite.app.BankKeeper.GetBalance(suite.ctx, lockOwner, "stake").String())
Expand All @@ -336,7 +358,7 @@ func (suite *KeeperTestSuite) TestPerpetualGaugeOperations() {
// distributing twice with adding more for perpetual gauge
gauge, err = suite.app.IncentivesKeeper.GetGaugeByID(suite.ctx, gaugeID)
suite.Require().NoError(err)
distrCoins, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins{sdk.NewInt64Coin("stake", 200)})

Expand Down Expand Up @@ -392,7 +414,7 @@ func (suite *KeeperTestSuite) TestNoLockPerpetualGaugeDistribution() {
suite.Require().NoError(err)

// distribute coins to stakers, since it's perpetual distribute everything on single distribution
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins(nil))

Expand Down Expand Up @@ -437,7 +459,7 @@ func (suite *KeeperTestSuite) TestNoLockNonPerpetualGaugeDistribution() {
suite.Require().NoError(err)

// distribute coins to stakers, since it's perpetual distribute everything on single distribution
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
distrCoins, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)
suite.Require().Equal(distrCoins, sdk.Coins(nil))

Expand Down Expand Up @@ -482,7 +504,7 @@ func (suite *KeeperTestSuite) TestGaugesByDenom() {
suite.Require().Len(gaugeIds, 0)

// distribute coins to stakers
_, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, *gauge)
_, err = suite.app.IncentivesKeeper.Distribute(suite.ctx, []types.Gauge{*gauge})
suite.Require().NoError(err)

// finish distribution for non perpetual gauge
Expand Down

0 comments on commit 58f5ea3

Please sign in to comment.