Skip to content

Commit

Permalink
Scale quadratic matching pool size (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
shanev authored Aug 17, 2020
1 parent b82fb72 commit 7b9ee3b
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 82 deletions.
2 changes: 1 addition & 1 deletion x/curating/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func EndBlocker(ctx sdk.Context, k Keeper) {
panic(err)
}

qv := NewQVFData()
qv := NewQVFData(ctx, k)

// iterate upvoters, returning deposits, and tallying upvotes
k.IterateUpvotes(ctx, post.VendorID, post.PostIDHash,
Expand Down
68 changes: 50 additions & 18 deletions x/curating/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,16 @@ import (
"github.com/stretchr/testify/require"
)

// initial state
// creator = 10 FUEL
// curator1 = 10 FUEL, upvote 1 FUEL
// curator2 = 10 FUEL, upvote 9 FUEL
//
// qvf
// voting_pool = 10 FUEL
// root_sum = 4
// match_pool = 4^2 - 10 = 6
// voter_reward = 5 FUEL
// match_reward = match_pool / 2 = 3
func TestEndBlockerExpiringPost(t *testing.T) {
var addrs = []sdk.AccAddress{}

func setup(t *testing.T) (*testdata.SimApp, sdk.Context) {
_, app, ctx := testdata.CreateTestInput()

postID := "500"
vendorID := uint32(1)

deposit := sdk.NewInt64Coin("ufuel", 1_000_000)
addrs := testdata.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(10_000_000))
addrs = testdata.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(10_000_000))

err := app.CuratingKeeper.CreatePost(
ctx, vendorID, postID, "body string", deposit, addrs[0], addrs[0])
Expand Down Expand Up @@ -69,23 +60,64 @@ func TestEndBlockerExpiringPost(t *testing.T) {
app.CuratingKeeper.GetParams(ctx).CurationWindow)
ctx = ctx.WithBlockHeader(h)

return app, ctx
}

// initial state
// creator = 10 FUEL
// curator1 = 10 FUEL, upvote 1 FUEL
// curator2 = 10 FUEL, upvote 9 FUEL
//
// qvf
// voting_pool = 10 FUEL
// root_sum = 4
// match_pool = 4^2 - 10 = 6
// voter_reward = 5 FUEL
// match_reward = match_pool / 2 = 3
func TestEndBlockerExpiringPost(t *testing.T) {
app, ctx := setup(t)

// add funds to reward pool
funds := sdk.NewInt64Coin("ufuel", 10_000_000)
err = app.BankKeeper.MintCoins(ctx, curating.RewardPoolName, sdk.NewCoins(funds))
funds := sdk.NewInt64Coin("ufuel", 10_000_000_000)
err := app.BankKeeper.MintCoins(ctx, curating.RewardPoolName, sdk.NewCoins(funds))
require.NoError(t, err)

curating.EndBlocker(ctx, app.CuratingKeeper)

// creator match reward = 0.5 * match_reward = 3 FUEL
creatorBal = app.BankKeeper.GetBalance(ctx, addrs[0], "ufuel")
creatorBal := app.BankKeeper.GetBalance(ctx, addrs[0], "ufuel")
require.Equal(t, "13000000", creatorBal.Amount.String(),
"10 (initial) + 3 (creator match reward)")

curator1Bal = app.BankKeeper.GetBalance(ctx, addrs[1], "ufuel")
curator1Bal := app.BankKeeper.GetBalance(ctx, addrs[1], "ufuel")
require.Equal(t, "15500000", curator1Bal.Amount.String(),
"8 (bal) + 1 (deposit) + 5 (voting reward) + 1.5 (match reward)")

curator2Bal = app.BankKeeper.GetBalance(ctx, addrs[2], "ufuel")
curator2Bal := app.BankKeeper.GetBalance(ctx, addrs[2], "ufuel")
require.Equal(t, "7500000", curator2Bal.Amount.String(),
"0 (bal) + 1 (deposit) + 5 (voter reward) + 1.5 (match reward)")
}

func TestEndBlockerExpiringPostWithSmolRewardPool(t *testing.T) {
app, ctx := setup(t)

// add funds to reward pool
funds := sdk.NewInt64Coin("ufuel", 1_000_000)
err := app.BankKeeper.MintCoins(ctx, curating.RewardPoolName, sdk.NewCoins(funds))
require.NoError(t, err)

curating.EndBlocker(ctx, app.CuratingKeeper)

// creator match reward = 0.5 * match_reward = 3 FUEL
creatorBal := app.BankKeeper.GetBalance(ctx, addrs[0], "ufuel")
require.Equal(t, "10000500", creatorBal.Amount.String(),
"10 (initial) + 3 (creator match reward)")

curator1Bal := app.BankKeeper.GetBalance(ctx, addrs[1], "ufuel")
require.Equal(t, "14000249", curator1Bal.Amount.String(),
"8 (bal) + 1 (deposit) + 5 (voting reward) + 1.5 (match reward)")

curator2Bal := app.BankKeeper.GetBalance(ctx, addrs[2], "ufuel")
require.Equal(t, "6000249", curator2Bal.Amount.String(),
"0 (bal) + 1 (deposit) + 5 (voter reward) + 1.5 (match reward)")
}
5 changes: 5 additions & 0 deletions x/curating/keeper/reward_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ func (k Keeper) GetRewardPool(ctx sdk.Context) (rewardPool authexported.ModuleAc
return k.accountKeeper.GetModuleAccount(ctx, types.RewardPoolName)
}

// GetRewardPoolBalance returns the reward pool balance
func (k Keeper) GetRewardPoolBalance(ctx sdk.Context) sdk.Coin {
return k.bankKeeper.GetBalance(ctx, k.GetRewardPool(ctx).GetAddress(), types.DefaultStakeDenom)
}

// InflateRewardPool process the designated inflation to the reward pool
func (k Keeper) InflateRewardPool(ctx sdk.Context) error {
blockInflationAddr := k.accountKeeper.GetModuleAccount(ctx, auth.FeeCollectorName).GetAddress()
Expand Down
42 changes: 28 additions & 14 deletions x/curating/quadratic.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,63 @@ import (

// QVFData holds vars for quadratic voting+funding calculations
type QVFData struct {
VoterCount int64
VotingPool sdk.Int
RootSum sdk.Dec
ctx sdk.Context
keeper Keeper
voterCount int64
votingPool sdk.Int
rootSum sdk.Dec
}

func NewQVFData() QVFData {
func NewQVFData(ctx sdk.Context, k Keeper) QVFData {
return QVFData{
VotingPool: sdk.ZeroInt(),
RootSum: sdk.ZeroDec(),
ctx: ctx,
keeper: k,
votingPool: sdk.ZeroInt(),
rootSum: sdk.ZeroDec(),
}
}

// TallyVote tallies a vote
func (q QVFData) TallyVote(amount sdk.Int) (QVFData, error) {
q.VoterCount++
q.VotingPool = q.VotingPool.Add(amount)
q.voterCount++
q.votingPool = q.votingPool.Add(amount)

sqrt, err := amount.ToDec().ApproxSqrt()
if err != nil {
return q, err
}
q.RootSum = q.RootSum.Add(sqrt)
q.rootSum = q.rootSum.Add(sqrt)

return q, nil
}

// MatchPool calculates and returns the quadratic match pool
func (q QVFData) MatchPool() sdk.Dec {
return q.RootSum.Power(2).Sub(q.VotingPool.ToDec())
idealPoolSize := q.rootSum.Power(2).Sub(q.votingPool.ToDec())

rewardPool := q.keeper.GetRewardPoolBalance(q.ctx).Amount.ToDec()
maxPoolPercent := q.keeper.GetParams(q.ctx).RewardPoolCurationMaxAlloc
maxPoolSize := rewardPool.MulTruncate(maxPoolPercent)

if idealPoolSize.GT(maxPoolSize) {
return maxPoolSize
}

return idealPoolSize
}

// VoterReward returns the distribution for a voter
func (q QVFData) VoterReward() sdk.Int {
if q.VoterCount == 0 {
if q.voterCount == 0 {
return sdk.ZeroInt()
}
return q.VotingPool.QuoRaw(q.VoterCount)
return q.votingPool.QuoRaw(q.voterCount)
}

// MatchReward returns the funding match for a voter
func (q QVFData) MatchReward() sdk.Dec {
if q.VoterCount == 0 {
if q.voterCount == 0 {
return sdk.ZeroDec()
}
return q.MatchPool().QuoInt64(q.VoterCount)
return q.MatchPool().QuoInt64(q.voterCount)
}
35 changes: 26 additions & 9 deletions x/curating/quadratic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/public-awesome/stakebird/testdata"
"github.com/public-awesome/stakebird/x/curating"
"github.com/stretchr/testify/require"
)
Expand All @@ -14,26 +15,42 @@ import (
// 4 votes = 16 vote credits
// 5 votes = 25 vote credits
func TestQVF(t *testing.T) {
q := curating.NewQVFData()
q, err := q.TallyVote(sdk.NewInt(1))
_, app, ctx := testdata.CreateTestInput()

// add funds to reward pool
funds := sdk.NewInt64Coin("ufuel", 10_000_000)
err := app.BankKeeper.MintCoins(ctx, curating.RewardPoolName, sdk.NewCoins(funds))
require.NoError(t, err)

q := curating.NewQVFData(ctx, app.CuratingKeeper)
q, err = q.TallyVote(sdk.NewInt(1))
require.NoError(t, err)
q, err = q.TallyVote(sdk.NewInt(9))
require.NoError(t, err)

require.Equal(t, int64(2), q.VoterCount)
require.Equal(t, "10", q.VotingPool.String())
require.Equal(t, "4.000000000000000000", q.RootSum.String())
// for reference...
// require.Equal(t, int64(2), q.VoterCount)
// require.Equal(t, "10", q.VotingPool.String())
// require.Equal(t, "4.000000000000000000", q.RootSum.String())
require.Equal(t, "6.000000000000000000", q.MatchPool().String())
require.Equal(t, "5", q.VoterReward().String())
require.Equal(t, "3.000000000000000000", q.MatchReward().String())
}

func TestQVFZeroVotes(t *testing.T) {
q := curating.NewQVFData()
_, app, ctx := testdata.CreateTestInput()

// add funds to reward pool
funds := sdk.NewInt64Coin("ufuel", 10_000_000)
err := app.BankKeeper.MintCoins(ctx, curating.RewardPoolName, sdk.NewCoins(funds))
require.NoError(t, err)

q := curating.NewQVFData(ctx, app.CuratingKeeper)

require.Equal(t, int64(0), q.VoterCount)
require.Equal(t, "0", q.VotingPool.String())
require.Equal(t, "0.000000000000000000", q.RootSum.String())
// for reference...
// require.Equal(t, int64(0), q.VoterCount)
// require.Equal(t, "0", q.VotingPool.String())
// require.Equal(t, "0.000000000000000000", q.RootSum.String())
require.Equal(t, "0.000000000000000000", q.MatchPool().String())
require.Equal(t, "0", q.VoterReward().String())
require.Equal(t, "0.000000000000000000", q.MatchReward().String())
Expand Down
Loading

0 comments on commit 7b9ee3b

Please sign in to comment.