diff --git a/Dockerfile b/Dockerfile index 3968a6ee1..2a1e74c62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ WORKDIR /code COPY . /code/ # See https://github.com/CosmWasm/wasmvm/releases -ADD https://github.com/CosmWasm/wasmvm/releases/download/v0.16.0/libwasmvm_muslc.a /lib/libwasmvm_muslc.a -RUN sha256sum /lib/libwasmvm_muslc.a | grep ef294a7a53c8d0aa6a8da4b10e94fb9f053f9decf160540d6c7594734bc35cd6 +ADD https://github.com/CosmWasm/wasmvm/releases/download/v0.16.1/libwasmvm_muslc.a /lib/libwasmvm_muslc.a +RUN sha256sum /lib/libwasmvm_muslc.a | grep 0e62296b9f24cf3a05f8513f99cee536c7087079855ea6ffb4f89b35eccdaa66 # force it to use static lib (from above) not standard libgo_cosmwasm.so file RUN LEDGER_ENABLED=false BUILD_TAGS=muslc make build diff --git a/shared.Dockerfile b/shared.Dockerfile index f805b7bbf..21b209198 100644 --- a/shared.Dockerfile +++ b/shared.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.15-buster AS go-builder +FROM golang:1.16-buster AS go-builder # Install minimum necessary dependencies, build Cosmos SDK, remove packages RUN apt update diff --git a/x/oracle/abci.go b/x/oracle/abci.go index c973ccbe6..de3caa58f 100644 --- a/x/oracle/abci.go +++ b/x/oracle/abci.go @@ -98,7 +98,13 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) { } // Distribute rewards to ballot winners - k.RewardBallotWinners(ctx, validatorClaimMap) + k.RewardBallotWinners( + ctx, + (int64)(params.VotePeriod), + (int64)(params.RewardDistributionWindow), + voteTargets, + validatorClaimMap, + ) // Clear the ballot k.ClearBallots(ctx, params.VotePeriod) diff --git a/x/oracle/keeper/alias_functions.go b/x/oracle/keeper/alias_functions.go index c8abad685..198e5fadd 100644 --- a/x/oracle/keeper/alias_functions.go +++ b/x/oracle/keeper/alias_functions.go @@ -13,7 +13,13 @@ func (k Keeper) GetOracleAccount(ctx sdk.Context) authtypes.ModuleAccountI { } // GetRewardPool retrieves the balance of the oracle module account -func (k Keeper) GetRewardPool(ctx sdk.Context) sdk.Coins { +func (k Keeper) GetRewardPool(ctx sdk.Context, denom string) sdk.Coin { + acc := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName) + return k.bankKeeper.GetBalance(ctx, acc.GetAddress(), denom) +} + +// GetRewardPool retrieves the balance of the oracle module account +func (k Keeper) GetRewardPoolLegacy(ctx sdk.Context) sdk.Coins { acc := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName) return k.bankKeeper.GetAllBalances(ctx, acc.GetAddress()) } diff --git a/x/oracle/keeper/keeper_test.go b/x/oracle/keeper/keeper_test.go index 6360461c9..443f5648c 100644 --- a/x/oracle/keeper/keeper_test.go +++ b/x/oracle/keeper/keeper_test.go @@ -97,8 +97,8 @@ func TestRewardPool(t *testing.T) { panic(err) // never occurs } - KFees := input.OracleKeeper.GetRewardPool(input.Ctx) - require.Equal(t, fees, KFees) + KFees := input.OracleKeeper.GetRewardPool(input.Ctx, core.MicroSDRDenom) + require.Equal(t, fees[0], KFees) } func TestParams(t *testing.T) { diff --git a/x/oracle/keeper/reward.go b/x/oracle/keeper/reward.go index 8c0c55699..037715f39 100644 --- a/x/oracle/keeper/reward.go +++ b/x/oracle/keeper/reward.go @@ -12,7 +12,88 @@ import ( // RewardBallotWinners implements // at the end of every VotePeriod, we give out portion of seigniorage reward(reward-weight) to the // oracle voters that voted faithfully. -func (k Keeper) RewardBallotWinners(ctx sdk.Context, ballotWinners map[string]types.Claim) { +func (k Keeper) RewardBallotWinners( + ctx sdk.Context, + votePeriod int64, + rewardDistributionWindow int64, + voteTargets map[string]sdk.Dec, + ballotWinners map[string]types.Claim, +) { + // softfork for reward distribution + if (ctx.ChainID() == "columbus-5" && ctx.BlockHeight() < int64(5_100_000)) || + (ctx.ChainID() == "bombay-12" && ctx.BlockHeight() < int64(6_200_000)) { + k.RewardBallotWinnersLegacy(ctx, votePeriod, rewardDistributionWindow, ballotWinners) + return + } + + rewardDenoms := make([]string, len(voteTargets)+1) + rewardDenoms[0] = core.MicroLunaDenom + + i := 1 + for denom := range voteTargets { + rewardDenoms[i] = denom + i++ + } + + // Sum weight of the claims + ballotPowerSum := int64(0) + for _, winner := range ballotWinners { + ballotPowerSum += winner.Weight + } + + // Exit if the ballot is empty + if ballotPowerSum == 0 { + return + } + + distributionRatio := sdk.NewDec(votePeriod).QuoInt64(rewardDistributionWindow) + + var periodRewards sdk.DecCoins + for _, denom := range rewardDenoms { + rewardPool := k.GetRewardPool(ctx, denom) + + // return if there's no rewards to give out + if rewardPool.IsZero() { + continue + } + + periodRewards = periodRewards.Add(sdk.NewDecCoinFromDec( + denom, + sdk.NewDecFromInt(rewardPool.Amount).Mul(distributionRatio), + )) + } + + // Dole out rewards + var distributedReward sdk.Coins + for _, winner := range ballotWinners { + receiverVal := k.StakingKeeper.Validator(ctx, winner.Recipient) + + // Reflects contribution + rewardCoins, _ := periodRewards.MulDec(sdk.NewDec(winner.Weight).QuoInt64(ballotPowerSum)).TruncateDecimal() + + // In case absence of the validator, we just skip distribution + if receiverVal != nil && !rewardCoins.IsZero() { + k.distrKeeper.AllocateTokensToValidator(ctx, receiverVal, sdk.NewDecCoinsFromCoins(rewardCoins...)) + distributedReward = distributedReward.Add(rewardCoins...) + } + } + + // Move distributed reward to distribution module + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.distrName, distributedReward) + if err != nil { + panic(fmt.Sprintf("[oracle] Failed to send coins to distribution module %s", err.Error())) + } + +} + +// RewardBallotWinnersLegacy implements +// at the end of every VotePeriod, we give out portion of seigniorage reward(reward-weight) to the +// oracle voters that voted faithfully. +func (k Keeper) RewardBallotWinnersLegacy( + ctx sdk.Context, + votePeriod int64, + rewardDistributionWindow int64, + ballotWinners map[string]types.Claim) { // Sum weight of the claims ballotPowerSum := int64(0) for _, winner := range ballotWinners { @@ -24,7 +105,7 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, ballotWinners map[string]ty return } - rewardPool := k.GetRewardPool(ctx) + rewardPool := k.GetRewardPoolLegacy(ctx) // return if there's no rewards to give out if rewardPool.IsZero() { @@ -33,7 +114,7 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, ballotWinners map[string]ty // rewardCoin = oraclePool * VotePeriod / RewardDistributionWindow periodRewards := sdk.NewDecFromInt(rewardPool.AmountOf(core.MicroLunaDenom)). - MulInt64((int64)(k.VotePeriod(ctx))).QuoInt64((int64)(k.RewardDistributionWindow(ctx))) + MulInt64(votePeriod).QuoInt64(rewardDistributionWindow) // Dole out rewards var distributedReward sdk.Coins diff --git a/x/oracle/keeper/reward_test.go b/x/oracle/keeper/reward_test.go index 60941e31b..1df323e66 100644 --- a/x/oracle/keeper/reward_test.go +++ b/x/oracle/keeper/reward_test.go @@ -49,22 +49,32 @@ func TestRewardBallotWinners(t *testing.T) { } // Prepare reward pool - givingAmt := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 30000000)) + givingAmt := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 30000000), sdk.NewInt64Coin(core.MicroUSDDenom, 40000000)) acc := input.AccountKeeper.GetModuleAccount(ctx, types.ModuleName) err = FundAccount(input, acc.GetAddress(), givingAmt) require.NoError(t, err) + voteTargets := make(map[string]sdk.Dec) + input.OracleKeeper.IterateTobinTaxes(ctx, func(denom string, tobinTax sdk.Dec) bool { + voteTargets[denom] = tobinTax + return false + }) + votePeriodsPerWindow := sdk.NewDec((int64)(input.OracleKeeper.RewardDistributionWindow(input.Ctx))). QuoInt64((int64)(input.OracleKeeper.VotePeriod(input.Ctx))). TruncateInt64() - input.OracleKeeper.RewardBallotWinners(ctx, claims) + input.OracleKeeper.RewardBallotWinners(ctx, (int64)(input.OracleKeeper.VotePeriod(input.Ctx)), (int64)(input.OracleKeeper.RewardDistributionWindow(input.Ctx)), voteTargets, claims) outstandingRewardsDec := input.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, addr) outstandingRewards, _ := outstandingRewardsDec.TruncateDecimal() require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).QuoInt64(votePeriodsPerWindow).QuoInt64(3).TruncateInt(), outstandingRewards.AmountOf(core.MicroLunaDenom)) + require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroUSDDenom)).QuoInt64(votePeriodsPerWindow).QuoInt64(3).TruncateInt(), + outstandingRewards.AmountOf(core.MicroUSDDenom)) outstandingRewardsDec1 := input.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, addr1) outstandingRewards1, _ := outstandingRewardsDec1.TruncateDecimal() require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).QuoInt64(votePeriodsPerWindow).QuoInt64(3).MulInt64(2).TruncateInt(), outstandingRewards1.AmountOf(core.MicroLunaDenom)) + require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroUSDDenom)).QuoInt64(votePeriodsPerWindow).QuoInt64(3).MulInt64(2).TruncateInt(), + outstandingRewards1.AmountOf(core.MicroUSDDenom)) }