Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only do one massive send for all gauge distribution #510

Merged
merged 2 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
96 changes: 30 additions & 66 deletions x/incentives/keeper/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,57 +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...)
if err := k.setGauge(ctx, &gauge); err != nil {
return nil, err
}

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 @@ -369,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 @@ -380,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 @@ -411,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 @@ -436,22 +384,38 @@ 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...)
if err := k.setGauge(ctx, &gauge); err != nil {
return nil, err
}

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
57 changes: 39 additions & 18 deletions x/incentives/keeper/gauge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +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 {
_, err := suite.app.IncentivesKeeper.Distribute(suite.ctx, g)
suite.Require().NoError(err)
}
_, 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 @@ -66,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 @@ -101,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 @@ -110,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 @@ -132,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 @@ -141,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 @@ -207,7 +228,7 @@ func (suite *KeeperTestSuite) TestNonPerpetualGaugeOperations() {
suite.Require().Len(gauges, 0)

// 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 Down Expand Up @@ -310,16 +331,16 @@ 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)})

// 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())

// add to gauge
addCoins := sdk.Coins{sdk.NewInt64Coin("stake", 200)}
Expand All @@ -328,7 +349,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 @@ -384,7 +405,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 @@ -429,7 +450,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 @@ -474,7 +495,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
Loading