Skip to content

Commit

Permalink
Part 7 - Delegator distribution for block rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
kinesis-agent committed Sep 6, 2023
1 parent 5c32884 commit 463e578
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 22 deletions.
19 changes: 19 additions & 0 deletions x/nodes/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,25 @@ func (k Keeper) splitRewards(
return
}

// Split feeCollector's cut into DAO and Propower
func (k Keeper) splitFeesCollected(
ctx sdk.Ctx,
feesCollected sdk.BigInt,
) (daoCut, proposerCut sdk.BigInt) {
daoAllocation := sdk.NewDec(k.DAOAllocation(ctx))
proposerAllocation := sdk.NewDec(k.ProposerAllocation(ctx))

// get the new percentages of `dao / (dao + proposer)`
daoAllocation = daoAllocation.Quo(daoAllocation.Add(proposerAllocation))

// dao cut calculation truncates int ex: 1.99uPOKT = 1uPOKT
daoCut = feesCollected.ToDec().Mul(daoAllocation).TruncateInt()

// proposer is whatever is left
proposerCut = feesCollected.Sub(daoCut)
return
}

// DAOAllocation - Retrieve DAO allocation
func (k Keeper) DAOAllocation(ctx sdk.Ctx) (res int64) {
k.Paramstore.Get(ctx, types.KeyDAOAllocation, &res)
Expand Down
58 changes: 41 additions & 17 deletions x/nodes/keeper/reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,38 +184,62 @@ func (k Keeper) GetChainSpecificMultiplier(ctx sdk.Ctx, chain string) sdk.BigInt
func (k Keeper) blockReward(ctx sdk.Ctx, previousProposer sdk.Address) {
feesCollector := k.getFeePool(ctx)
feesCollected := feesCollector.GetCoins().AmountOf(sdk.DefaultStakeDenom)
// check for zero fees
if feesCollected.IsZero() {
return
}
// get the dao and proposer % ex DAO .1 or 10% Proposer .01 or 1%
daoAllocation := sdk.NewDec(k.DAOAllocation(ctx))
proposerAllocation := sdk.NewDec(k.ProposerAllocation(ctx))
daoAndProposerAllocation := daoAllocation.Add(proposerAllocation)
// get the new percentages based on the total. This is needed because the node (relayer) cut has already been allocated
daoAllocation = daoAllocation.Quo(daoAndProposerAllocation)
// dao cut calculation truncates int ex: 1.99uPOKT = 1uPOKT
daoCut := feesCollected.ToDec().Mul(daoAllocation).TruncateInt()
// proposer is whatever is left
proposerCut := feesCollected.Sub(daoCut)

daoCut, proposerCut := k.splitFeesCollected(ctx, feesCollected)

// send to the two parties
feeAddr := feesCollector.GetAddress()
err := k.AccountKeeper.SendCoinsFromAccountToModule(ctx, feeAddr, govTypes.DAOAccountName, sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, daoCut)))
if err != nil {
ctx.Logger().Error(fmt.Sprintf("unable to send %s cut of block reward to the dao: %s, at height %d", daoCut.String(), err.Error(), ctx.BlockHeight()))
ctx.Logger().Error("unable to send a DAO cut of block reward",
"height", ctx.BlockHeight(),
"cut", daoCut,
"err", err.Error(),
)
}

if k.Cdc.IsAfterNonCustodialUpgrade(ctx.BlockHeight()) {
outputAddress, found := k.GetValidatorOutputAddress(ctx, previousProposer)
validator, found := k.GetValidator(ctx, previousProposer)
if !found {
ctx.Logger().Error(fmt.Sprintf("unable to send %s cut of block reward to the proposer: %s, with error %s, at height %d", proposerCut.String(), previousProposer, types.ErrNoValidatorForAddress(types.ModuleName), ctx.BlockHeight()))
ctx.Logger().Error("unable to find a validator to send a block reward to",
"height", ctx.BlockHeight(),
"addr", previousProposer,
)
return
}
err = k.AccountKeeper.SendCoins(ctx, feeAddr, outputAddress, sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, proposerCut)))
if err != nil {
ctx.Logger().Error(fmt.Sprintf("unable to send %s cut of block reward to the proposer: %s, with error %s, at height %d", proposerCut.String(), previousProposer, err.Error(), ctx.BlockHeight()))

if !k.Cdc.IsAfterDelegatorUpgrade(ctx.BlockHeight()) {
validator.Delegators = nil
}

SplitNodeRewards(
proposerCut,
k.GetOutputAddressFromValidator(validator),
validator.Delegators,
func(recipient sdk.Address, share sdk.BigInt) {
err = k.AccountKeeper.SendCoins(
ctx,
feeAddr,
recipient,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, share)),
)
if err != nil {
ctx.Logger().Error("unable to send a cut of block reward",
"height", ctx.BlockHeight(),
"cut", share,
"addr", recipient,
"err", err.Error(),
)
}
},
)

return
}

err = k.AccountKeeper.SendCoins(ctx, feeAddr, previousProposer, sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, proposerCut)))
if err != nil {
ctx.Logger().Error(fmt.Sprintf("unable to send %s cut of block reward to the proposer: %s, with error %s, at height %d", proposerCut.String(), previousProposer, err.Error(), ctx.BlockHeight()))
Expand Down
20 changes: 15 additions & 5 deletions x/nodes/keeper/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ func TestKeeper_rewardFromFees(t *testing.T) {
fp = keeper.getFeePool(context)
keeper.SetValidator(context, stakedValidator)
assert.Equal(t, fees, fp.GetCoins())

_, proposerCut := keeper.splitFeesCollected(context, amount)

totalSupplyPrev := keeper.AccountKeeper.GetSupply(context).
GetTotal().
AmountOf("upokt")

tests := []struct {
name string
fields fields
Expand All @@ -159,11 +166,14 @@ func TestKeeper_rewardFromFees(t *testing.T) {
k := tt.fields.keeper
ctx := tt.args.ctx
k.blockReward(tt.args.ctx, tt.args.previousProposer)
acc := k.GetAccount(ctx, tt.args.Output)
assert.False(t, acc.Coins.IsZero())
assert.True(t, acc.Coins.IsEqual(sdk.NewCoins(sdk.NewCoin("upokt", sdk.NewInt(910)))))
acc = k.GetAccount(ctx, tt.args.previousProposer)
assert.True(t, acc.Coins.IsZero())

verifyAccountBalance(t, k, ctx, tt.args.Output, proposerCut)
verifyAccountBalance(t, k, ctx, tt.args.previousProposer, sdk.ZeroInt())

totalSupply := k.AccountKeeper.GetSupply(ctx).
GetTotal().
AmountOf("upokt")
assert.True(t, totalSupply.Equal(totalSupplyPrev))
})
}
}
Expand Down

0 comments on commit 463e578

Please sign in to comment.