cleanup begin/end block (#553)

* cleanup begin/end block

* update cdp BeginBlocker

* update dist mint

* fix tests

* typo

* add comment on CloseExpiredAuctions

* fix tests
This commit is contained in:
Federico Kunze 2020-06-17 11:09:44 +02:00 committed by GitHub
parent f0e73e4aa8
commit bfb9f7e20a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 166 additions and 141 deletions

View File

@ -1,13 +1,17 @@
package auction
import (
"errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/auction/types"
)
// BeginBlocker runs at the start of every block.
// BeginBlocker closes all expired auctions at the end of each block. It panics if
// there's an error other than ErrAuctionNotFound.
func BeginBlocker(ctx sdk.Context, k Keeper) {
err := k.CloseExpiredAuctions(ctx)
if err != nil {
if err != nil && !errors.Is(err, types.ErrAuctionNotFound) {
panic(err)
}
}

View File

@ -1,6 +1,7 @@
package keeper
import (
"errors"
"fmt"
"time"
@ -449,7 +450,6 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, auction types.DebtAuction, bidder
// CloseAuction closes an auction and distributes funds to the highest bidder.
func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) error {
auction, found := k.GetAuction(ctx, auctionID)
if !found {
return sdkerrors.Wrapf(types.ErrAuctionNotFound, "%d", auctionID)
@ -460,21 +460,20 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) error {
}
// payout to the last bidder
var err error
switch auc := auction.(type) {
case types.SurplusAuction:
if err := k.PayoutSurplusAuction(ctx, auc); err != nil {
return err
}
err = k.PayoutSurplusAuction(ctx, auc)
case types.DebtAuction:
if err := k.PayoutDebtAuction(ctx, auc); err != nil {
return err
}
err = k.PayoutDebtAuction(ctx, auc)
case types.CollateralAuction:
if err := k.PayoutCollateralAuction(ctx, auc); err != nil {
return err
}
err = k.PayoutCollateralAuction(ctx, auc)
default:
return sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auc.GetType())
err = sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auc.GetType())
}
if err != nil {
return err
}
k.DeleteAuction(ctx, auctionID)
@ -502,23 +501,17 @@ func (k Keeper) PayoutDebtAuction(ctx sdk.Context, auction types.DebtAuction) er
return err
}
// if there is remaining debt, return it to the calling module to manage
if auction.CorrespondingDebt.IsPositive() {
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
if err != nil {
return err
}
if !auction.CorrespondingDebt.IsPositive() {
return nil
}
return nil
return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
}
// PayoutSurplusAuction pays out the proceeds for a surplus auction.
func (k Keeper) PayoutSurplusAuction(ctx sdk.Context, auction types.SurplusAuction) error {
// Send the tokens from the auction module account where they are being managed to the bidder who won the auction
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, auction.Bidder, sdk.NewCoins(auction.Lot))
if err != nil {
return err
}
return nil
return k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, auction.Bidder, sdk.NewCoins(auction.Lot))
}
// PayoutCollateralAuction pays out the proceeds for a collateral auction.
@ -530,29 +523,30 @@ func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, auction types.Collatera
}
// if there is remaining debt after the auction, send it back to the initiating module for management
if auction.CorrespondingDebt.IsPositive() {
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
if err != nil {
return err
}
if !auction.CorrespondingDebt.IsPositive() {
return nil
}
return nil
return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, auction.Initiator, sdk.NewCoins(auction.CorrespondingDebt))
}
// CloseExpiredAuctions finds all auctions that are past (or at) their ending times and closes them, paying out to the highest bidder.
// CloseExpiredAuctions iterates over all the auctions stored by until the current
// block timestamp and that are past (or at) their ending times and closes them,
// paying out to the highest bidder.
func (k Keeper) CloseExpiredAuctions(ctx sdk.Context) error {
var expiredAuctions []uint64
k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) bool {
expiredAuctions = append(expiredAuctions, id)
var err error
k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) (stop bool) {
err = k.CloseAuction(ctx, id)
if err != nil && !errors.Is(err, types.ErrAuctionNotFound) {
// stop iteration
return true
}
// reset error in case the last element had an ErrAuctionNotFound
err = nil
return false
})
// Note: iteration and auction closing are in separate loops as db should not be modified during iteration // TODO is this correct? gov modifies during iteration
for _, id := range expiredAuctions {
if err := k.CloseAuction(ctx, id); err != nil {
return err
}
}
return nil
return err
}
// earliestTime returns the earliest of two times.

View File

@ -1,9 +1,13 @@
package cdp
import (
"errors"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
)
// BeginBlocker compounds the debt in outstanding cdps and liquidates cdps that are below the required collateralization ratio
@ -17,7 +21,6 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
}
for _, cp := range params.CollateralParams {
ok := k.UpdatePricefeedStatus(ctx, cp.SpotMarketID)
if !ok {
continue
@ -29,26 +32,30 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
}
err := k.UpdateFeesForAllCdps(ctx, cp.Denom)
if err != nil {
panic(err)
}
err = k.LiquidateCdps(ctx, cp.LiquidationMarketID, cp.Denom, cp.LiquidationRatio)
if err != nil {
if err != nil && !errors.Is(err, pricefeedtypes.ErrNoValidPrice) {
panic(err)
}
}
err := k.RunSurplusAndDebtAuctions(ctx)
if err != nil {
panic(err)
}
distTimeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousDistTime.Unix())
if !distTimeElapsed.GTE(sdk.NewInt(int64(params.SavingsDistributionFrequency.Seconds()))) {
return
}
err = k.DistributeSavingsRate(ctx, params.DebtParam.Denom)
if err != nil {
panic(err)
}
k.SetPreviousSavingsDistribution(ctx, ctx.BlockTime())
}

View File

@ -149,11 +149,13 @@ func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) error {
}
remainingDebt := k.GetTotalDebt(ctx, types.LiquidatorMacc)
params := k.GetParams(ctx)
if remainingDebt.GTE(params.DebtAuctionThreshold) {
debtLot := sdk.NewCoin(k.GetDebtDenom(ctx), params.DebtAuctionLot)
bidCoin := sdk.NewCoin(params.DebtParam.Denom, debtLot.Amount)
_, err := k.auctionKeeper.StartDebtAuction(
ctx, types.LiquidatorMacc, bidCoin, sdk.NewCoin(k.GetGovDenom(ctx), debtLot.Amount.Mul(sdk.NewInt(dump))), debtLot)
initialLot := sdk.NewCoin(k.GetGovDenom(ctx), debtLot.Amount.Mul(sdk.NewInt(dump)))
_, err := k.auctionKeeper.StartDebtAuction(ctx, types.LiquidatorMacc, bidCoin, initialLot, debtLot)
if err != nil {
return err
}
@ -163,6 +165,7 @@ func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) error {
if !surplus.GTE(params.SurplusAuctionThreshold) {
return nil
}
surplusLot := sdk.NewCoin(params.DebtParam.Denom, sdk.MinInt(params.SurplusAuctionLot, surplus))
_, err := k.auctionKeeper.StartSurplusAuction(ctx, types.LiquidatorMacc, surplusLot, k.GetGovDenom(ctx))
return err

View File

@ -26,8 +26,8 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coin, periods sdk.I
func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) error {
var iterationErr error
k.IterateCdpsByDenom(ctx, collateralDenom, func(cdp types.CDP) bool {
oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees))
// periods = bblock timestamp - fees updated
periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix()))
newFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralDenom)
@ -55,12 +55,14 @@ func (k Keeper) UpdateFeesForAllCdps(ctx sdk.Context, collateralDenom string) er
if (newFeesSavings.IsZero() && !savingsRate.IsZero()) || (newFeesSurplus.IsZero() && !savingsRate.Equal(sdk.OneDec())) {
return false
}
// mint debt coins to the cdp account
err := k.MintDebtCoins(ctx, types.ModuleName, k.GetDebtDenom(ctx), newFees)
if err != nil {
iterationErr = err
return true
}
previousDebt := k.GetTotalPrincipal(ctx, collateralDenom, dp.Denom)
newDebt := previousDebt.Add(newFees.Amount)
k.SetTotalPrincipal(ctx, collateralDenom, dp.Denom, newDebt)
@ -100,17 +102,14 @@ func (k Keeper) IncrementTotalPrincipal(ctx sdk.Context, collateralDenom string,
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
total = total.Add(principal.Amount)
k.SetTotalPrincipal(ctx, collateralDenom, principal.Denom, total)
}
// DecrementTotalPrincipal decrements the total amount of debt that has been drawn for a particular collateral type
func (k Keeper) DecrementTotalPrincipal(ctx sdk.Context, collateralDenom string, principal sdk.Coin) {
total := k.GetTotalPrincipal(ctx, collateralDenom, principal.Denom)
total = total.Sub(principal.Amount)
if total.IsNegative() {
// can happen in tests due to rounding errors in fee calculation
total = sdk.ZeroInt()
}
// NOTE: negative total principal can happen in tests due to rounding errors
// in fee calculation
total = sdk.MaxInt(total.Sub(principal.Amount), sdk.ZeroInt())
k.SetTotalPrincipal(ctx, collateralDenom, principal.Denom, total)
}

View File

@ -18,6 +18,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
if !found {
return sdkerrors.Wrap(types.ErrDebtNotSupported, debtDenom)
}
savingsRateMacc := k.supplyKeeper.GetModuleAccount(ctx, types.SavingsRateMacc)
surplusToDistribute := savingsRateMacc.GetCoins().AmountOf(dp.Denom)
if surplusToDistribute.IsZero() {
@ -26,21 +27,26 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
modAccountCoins := k.getModuleAccountCoins(ctx, dp.Denom)
totalSupplyLessModAccounts := k.supplyKeeper.GetSupply(ctx).GetTotal().Sub(modAccountCoins)
// values to use in interest calculation
totalSurplus := sdk.NewDecFromInt(surplusToDistribute)
totalSupply := sdk.NewDecFromInt(totalSupplyLessModAccounts.AmountOf(debtDenom))
var iterationErr error
// TODO: avoid iterating over all the accounts by keeping the stored stable coin
// holders' addresses separately.
k.accountKeeper.IterateAccounts(ctx, func(acc authexported.Account) (stop bool) {
_, ok := acc.(supplyexported.ModuleAccountI)
if ok {
// don't distribute savings rate to module accounts
return false
}
debtAmount := acc.GetCoins().AmountOf(debtDenom)
if !debtAmount.IsPositive() {
return false
}
// (balance * rewardToDisribute) / totalSupply
// interest is the ratable fraction of savings rate owed to that account, rounded using bankers rounding
interest := (sdk.NewDecFromInt(debtAmount).Mul(totalSurplus)).Quo(totalSupply).RoundInt()
@ -51,6 +57,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
if !interest.IsPositive() {
return false
}
interestCoins := sdk.NewCoins(sdk.NewCoin(debtDenom, interest))
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.SavingsRateMacc, acc.GetAddress(), interestCoins)
if err != nil {
@ -60,6 +67,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error {
surplusToDistribute = surplusToDistribute.Sub(interest)
return false
})
return iterationErr
}

View File

@ -24,9 +24,7 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
deposits := k.GetDeposits(ctx, cdp.ID)
debt := cdp.Principal.Amount.Add(cdp.AccumulatedFees.Amount)
modAccountDebt := k.getModAccountDebt(ctx, types.ModuleName)
if modAccountDebt.LT(debt) {
debt = modAccountDebt
}
debt = sdk.MinInt(debt, modAccountDebt)
debtCoin := sdk.NewCoin(k.GetDebtDenom(ctx), debt)
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(debtCoin))
if err != nil {
@ -35,6 +33,12 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
// liquidate deposits and send collateral from cdp to liquidator
for _, dep := range deposits {
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(dep.Amount))
if err != nil {
return err
}
k.DeleteDeposit(ctx, dep.CdpID, dep.Depositor)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeCdpLiquidation,
@ -43,12 +47,8 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
sdk.NewAttribute(types.AttributeKeyDeposit, dep.String()),
),
)
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(dep.Amount))
if err != nil {
return err
}
k.DeleteDeposit(ctx, dep.CdpID, dep.Depositor)
}
err = k.AuctionCollateral(ctx, deposits, debt, cdp.Principal.Denom)
if err != nil {
return err

View File

@ -8,9 +8,7 @@ import (
// BeginBlocker runs at the start of every block.
func BeginBlocker(ctx sdk.Context, _ abci.RequestBeginBlock, k Keeper) {
// enact proposals ignoring their expiry time - they could have received enough votes last block before expiring this block
k.EnactPassedProposals(ctx)
k.CloseExpiredProposals(ctx)
}

View File

@ -207,9 +207,7 @@ func (k Keeper) GetProposalsByCommittee(ctx sdk.Context, committeeID uint64) []t
// DeleteProposalAndVotes removes a proposal and its associated votes.
func (k Keeper) DeleteProposalAndVotes(ctx sdk.Context, proposalID uint64) {
votes := k.GetVotesByProposal(ctx, proposalID)
k.DeleteProposal(ctx, proposalID)
for _, v := range votes {
k.DeleteVote(ctx, v.ProposalID, v.Voter)

View File

@ -138,7 +138,9 @@ func (k Keeper) EnactPassedProposals(ctx sdk.Context) {
if err != nil {
panic(err)
}
if !passes {
// continue to next proposal
return false
}
@ -164,7 +166,6 @@ func (k Keeper) EnactPassedProposals(ctx sdk.Context) {
// CloseExpiredProposals removes proposals (and associated votes) that have past their deadline.
func (k Keeper) CloseExpiredProposals(ctx sdk.Context) {
k.IterateProposals(ctx, func(proposal types.Proposal) bool {
if !proposal.HasExpiredBy(ctx.BlockTime()) {
return false

View File

@ -48,7 +48,8 @@ func (k Keeper) CreateAndDeleteRewardPeriods(ctx sdk.Context) {
}
}
// ApplyRewardsToCdps iterates over the reward periods and creates a claim for each cdp owner that created usdx with the collateral specified in the reward period
// ApplyRewardsToCdps iterates over the reward periods and creates a claim for each
// cdp owner that created usdx with the collateral specified in the reward period.
func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
if !found {
@ -56,6 +57,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
k.SetPreviousBlockTime(ctx, previousBlockTime)
return
}
k.IterateRewardPeriods(ctx, func(rp types.RewardPeriod) bool {
expired := false
// the total amount of usdx created with the collateral type being incentivized
@ -66,6 +68,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
timeElapsed = sdk.NewInt(rp.End.Unix() - previousBlockTime.Unix())
expired = true
}
// the amount of rewards to pay (rewardAmount * timeElapsed)
rewardsThisPeriod := rp.Reward.Amount.Mul(timeElapsed)
id := k.GetNextClaimPeriodID(ctx, rp.Denom)
@ -85,6 +88,7 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
k.HandleRewardPeriodExpiry(ctx, rp)
return false
})
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
}

View File

@ -19,42 +19,44 @@ func (k Keeper) MintPeriodInflation(ctx sdk.Context) error {
)
return nil
}
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
if !found {
previousBlockTime = ctx.BlockTime()
k.SetPreviousBlockTime(ctx, previousBlockTime)
return nil
}
var err error
for _, period := range params.Periods {
switch {
// Case 1 - period is fully expired
if period.End.Before(previousBlockTime) {
case period.End.Before(previousBlockTime):
continue
}
// Case 2 - period has ended since the previous block time
if period.End.After(previousBlockTime) && period.End.Before(ctx.BlockTime()) {
case period.End.After(previousBlockTime) && period.End.Before(ctx.BlockTime()):
// calculate time elapsed relative to the periods end time
timeElapsed := sdk.NewInt(period.End.Unix() - previousBlockTime.Unix())
err := k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
if err != nil {
return err
}
err = k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
// update the value of previousBlockTime so that the next period starts from the end of the last
// period and not the original value of previousBlockTime
previousBlockTime = period.End
}
// Case 3 - period is ongoing
if (period.Start.Before(previousBlockTime) || period.Start.Equal(previousBlockTime)) && period.End.After(ctx.BlockTime()) {
case (period.Start.Before(previousBlockTime) || period.Start.Equal(previousBlockTime)) && period.End.After(ctx.BlockTime()):
// calculate time elapsed relative to the current block time
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
err := k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
if err != nil {
return err
}
}
err = k.mintInflationaryCoins(ctx, period.Inflation, timeElapsed, types.GovDenom)
// Case 4 - period hasn't started
if period.Start.After(ctx.BlockTime()) || period.Start.Equal(ctx.BlockTime()) {
case period.Start.After(ctx.BlockTime()) || period.Start.Equal(ctx.BlockTime()):
continue
}
if err != nil {
return err
}
}
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
return nil
@ -78,11 +80,13 @@ func (k Keeper) mintInflationaryCoins(ctx sdk.Context, inflationRate sdk.Dec, ti
if err != nil {
return err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeKavaDist,
sdk.NewAttribute(types.AttributeKeyInflation, sdk.NewCoin(denom, amountToMint).String()),
),
)
return nil
}

View File

@ -1,28 +1,23 @@
package pricefeed
import (
"fmt"
"errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/pricefeed/types"
)
// EndBlocker updates the current pricefeed
func EndBlocker(ctx sdk.Context, k Keeper) {
// Update the current price of each asset.
for _, market := range k.GetMarkets(ctx) {
if market.Active {
err := k.SetCurrentPrices(ctx, market.MarketID)
if err != nil {
// In the event of failure, emit an event.
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypeNoValidPrices,
sdk.NewAttribute(AttributeMarketID, fmt.Sprintf("%s", market.MarketID)),
),
)
continue
}
if !market.Active {
continue
}
err := k.SetCurrentPrices(ctx, market.MarketID)
if err != nil && !errors.Is(err, types.ErrNoValidPrice) {
panic(err)
}
}
return
}

View File

@ -1,9 +1,12 @@
package keeper
import (
"fmt"
"sort"
"time"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -37,6 +40,11 @@ func NewKeeper(
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// SetPrice updates the posted price for a specific oracle
func (k Keeper) SetPrice(
ctx sdk.Context,
@ -111,40 +119,41 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) error {
notExpiredPrices = append(notExpiredPrices, types.NewCurrentPrice(v.MarketID, v.Price))
}
}
if len(notExpiredPrices) == 0 {
store := ctx.KVStore(k.key)
store.Set(
types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}),
)
// NOTE: The current price stored will continue storing the most recent (expired)
// price if this is not set.
// This zero's out the current price stored value for that market and ensures
// that CDP methods that GetCurrentPrice will return error.
k.setCurrentPrice(ctx, marketID, types.CurrentPrice{})
return types.ErrNoValidPrice
}
medianPrice := k.CalculateMedianPrice(ctx, notExpiredPrices)
// check case that market price was not set in genesis
if validPrevPrice {
if validPrevPrice && !medianPrice.Equal(prevPrice.Price) {
// only emit event if price has changed
if !medianPrice.Equal(prevPrice.Price) {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeMarketPriceUpdated,
sdk.NewAttribute(types.AttributeMarketID, marketID),
sdk.NewAttribute(types.AttributeMarketPrice, medianPrice.String()),
),
)
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeMarketPriceUpdated,
sdk.NewAttribute(types.AttributeMarketID, marketID),
sdk.NewAttribute(types.AttributeMarketPrice, medianPrice.String()),
),
)
}
store := ctx.KVStore(k.key)
currentPrice := types.NewCurrentPrice(marketID, medianPrice)
store.Set(
types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(currentPrice),
)
k.setCurrentPrice(ctx, marketID, currentPrice)
return nil
}
func (k Keeper) setCurrentPrice(ctx sdk.Context, marketID string, currentPrice types.CurrentPrice) {
store := ctx.KVStore(k.key)
store.Set(types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(currentPrice))
}
// CalculateMedianPrice calculates the median prices for the input prices.
func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices types.CurrentPrices) sdk.Dec {
l := len(prices)

View File

@ -24,34 +24,35 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
var voteInfos VoteInfos
voteInfos = req.LastCommitInfo.GetVotes()
validatorVestingKeys := k.GetAllAccountKeys(ctx)
for _, key := range validatorVestingKeys {
acc := k.GetAccountFromAuthKeeper(ctx, key)
if k.AccountIsVesting(ctx, acc.GetAddress()) {
vote, found := voteInfos.FilterByValidatorAddress(acc.ValidatorAddress)
if !found || !vote.SignedLastBlock {
if ctx.BlockHeight() <= 1 {
// don't count missed blocks on block 1 since there is no vote history
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
} else {
// if the validator was not found or explicitly didn't sign, increment the missing sign count
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
}
} else {
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
}
// check if a period ended in the last block
endTimes := k.GetPeriodEndTimes(ctx, key)
for i, t := range endTimes {
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key, i)
}
}
// handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key, currentBlockTime)
if !k.AccountIsVesting(ctx, acc.GetAddress()) {
continue
}
vote, found := voteInfos.FilterByValidatorAddress(acc.ValidatorAddress)
var missedBlock bool
// if the validator was not found or explicitly didn't sign, increment the missing sign count
if (!found || !vote.SignedLastBlock) && ctx.BlockHeight() > 1 {
missedBlock = true
}
k.UpdateMissingSignCount(ctx, acc.GetAddress(), missedBlock)
// check if a period ended in the last block
endTimes := k.GetPeriodEndTimes(ctx, key)
for i, t := range endTimes {
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key, i)
}
}
// handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key, currentBlockTime)
}
k.SetPreviousBlockTime(ctx, currentBlockTime)
}

View File

@ -74,7 +74,7 @@ func TestBeginBlockerZeroHeight(t *testing.T) {
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
// require missed block counter doesn't increment because there's no voting history
require.Equal(t, types.CurrentPeriodProgress{0, 1}, vva.CurrentPeriodProgress)
require.Equal(t, types.CurrentPeriodProgress{MissedBlocks: 0, TotalBlocks: 1}, vva.CurrentPeriodProgress)
// mark the validator as having missed
req = abci.RequestBeginBlock{
@ -90,7 +90,7 @@ func TestBeginBlockerZeroHeight(t *testing.T) {
BeginBlocker(ctx, req, vvk)
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, types.CurrentPeriodProgress{0, 2}, vva.CurrentPeriodProgress)
require.Equal(t, types.CurrentPeriodProgress{MissedBlocks: 0, TotalBlocks: 2}, vva.CurrentPeriodProgress)
}
func TestBeginBlockerSignedBlock(t *testing.T) {