From 0e50b709177bede0991e52633d99f4f9b728dfe3 Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Tue, 21 Jan 2020 18:43:17 +0000 Subject: [PATCH] fix: avoid panic if cdp debt > debt held by cdp account --- x/cdp/abci_test.go | 54 ++++++++++++++++++++++++++++++++++++++ x/cdp/integration_test.go | 4 +-- x/cdp/keeper/seize.go | 9 +++++++ x/cdp/keeper/seize_test.go | 30 +++++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/x/cdp/abci_test.go b/x/cdp/abci_test.go index e8c9573f3c..b16ab7e51c 100644 --- a/x/cdp/abci_test.go +++ b/x/cdp/abci_test.go @@ -33,6 +33,32 @@ type liquidationTracker struct { } func (suite *ModuleTestSuite) SetupTest() { + tApp := app.NewTestApp() + ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) + coins := []sdk.Coins{} + tracker := liquidationTracker{} + + for j := 0; j < 100; j++ { + coins = append(coins, cs(c("btc", 100000000), c("xrp", 10000000000))) + } + _, addrs := app.GeneratePrivKeyAddressPairs(100) + + authGS := app.NewAuthGenState( + addrs, coins) + tApp.InitializeFromGenesisStates( + authGS, + NewPricefeedGenStateMulti(), + NewCDPGenStateMulti(), + ) + suite.ctx = ctx + suite.app = tApp + suite.keeper = tApp.GetCDPKeeper() + suite.cdps = cdp.CDPs{} + suite.addrs = addrs + suite.liquidations = tracker +} + +func (suite *ModuleTestSuite) createCdps() { tApp := app.NewTestApp() ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) cdps := make(cdp.CDPs, 100) @@ -96,6 +122,7 @@ func (suite *ModuleTestSuite) setPrice(price sdk.Dec, market string) { suite.Equal(price, pp.Price) } func (suite *ModuleTestSuite) TestBeginBlock() { + suite.createCdps() sk := suite.app.GetSupplyKeeper() acc := sk.GetModuleAccount(suite.ctx, cdp.ModuleName) originalXrpCollateral := acc.GetCoins().AmountOf("xrp") @@ -122,6 +149,33 @@ func (suite *ModuleTestSuite) TestBeginBlock() { } +func (suite *ModuleTestSuite) TestSeizeSingleCdpWithFees() { + err := suite.keeper.AddCdp(suite.ctx, suite.addrs[0], cs(c("xrp", 10000000000)), cs(c("usdx", 1000000000))) + suite.NoError(err) + suite.keeper.SetPreviousBlockTime(suite.ctx, suite.ctx.BlockTime()) + previousBlockTime, _ := suite.keeper.GetPreviousBlockTime(suite.ctx) + suite.Equal(i(1000000000), suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")) + sk := suite.app.GetSupplyKeeper() + cdpMacc := sk.GetModuleAccount(suite.ctx, cdp.ModuleName) + suite.Equal(i(1000000000), cdpMacc.GetCoins().AmountOf("debt")) + for i := 0; i < 100; i++ { + suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Second * 6)) + cdp.BeginBlocker(suite.ctx, abci.RequestBeginBlock{Header: suite.ctx.BlockHeader()}, suite.keeper) + } + + cdpMacc = sk.GetModuleAccount(suite.ctx, cdp.ModuleName) + suite.Equal(i(1000000900), (cdpMacc.GetCoins().AmountOf("debt"))) + cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp", 1) + + timeElapsed := sdk.NewInt(suite.ctx.BlockTime().Unix() - previousBlockTime.Unix()) + + fees := suite.keeper.CalculateFees(suite.ctx, cdp.Principal, timeElapsed, "xrp") + suite.Equal(i(928), fees.AmountOf("usdx")) + + err = suite.keeper.SeizeCollateral(suite.ctx, cdp) + suite.NoError(err) +} + func TestModuleTestSuite(t *testing.T) { suite.Run(t, new(ModuleTestSuite)) } diff --git a/x/cdp/integration_test.go b/x/cdp/integration_test.go index bb42900628..2ae0ae9be5 100644 --- a/x/cdp/integration_test.go +++ b/x/cdp/integration_test.go @@ -154,10 +154,10 @@ func NewCDPGenStateMulti() app.GenesisState { func cdps() (cdps cdp.CDPs) { _, addrs := app.GeneratePrivKeyAddressPairs(3) - c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(10000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(8000000))), tmtime.Canonical(time.Now())) + c1 := cdp.NewCDP(uint64(1), addrs[0], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(100000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(8000000))), tmtime.Canonical(time.Now())) c2 := cdp.NewCDP(uint64(2), addrs[1], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(100000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(10000000))), tmtime.Canonical(time.Now())) c3 := cdp.NewCDP(uint64(3), addrs[1], sdk.NewCoins(sdk.NewCoin("btc", sdk.NewInt(1000000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(10000000))), tmtime.Canonical(time.Now())) - c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(1000000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(500000000))), tmtime.Canonical(time.Now())) + c4 := cdp.NewCDP(uint64(4), addrs[2], sdk.NewCoins(sdk.NewCoin("xrp", sdk.NewInt(1000000000))), sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(50000000))), tmtime.Canonical(time.Now())) cdps = append(cdps, c1, c2, c3, c4) return } diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index fcb15756b2..0f69377acd 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -35,6 +35,10 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) sdk.Error { for _, dc := range cdp.AccumulatedFees { debt = debt.Add(dc.Amount) } + modAccountDebt := k.getModAccountDebt(ctx, types.ModuleName) + if modAccountDebt.LT(debt) { + debt = modAccountDebt + } debtCoin := sdk.NewCoin(k.GetDebtDenom(ctx), debt) err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(debtCoin)) if err != nil { @@ -108,3 +112,8 @@ func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, li } return nil } + +func (k Keeper) getModAccountDebt(ctx sdk.Context, accountName string) sdk.Int { + macc := k.supplyKeeper.GetModuleAccount(ctx, accountName) + return macc.GetCoins().AmountOf(k.GetDebtDenom(ctx)) +} diff --git a/x/cdp/keeper/seize_test.go b/x/cdp/keeper/seize_test.go index 0c89db6b38..db1099b1f8 100644 --- a/x/cdp/keeper/seize_test.go +++ b/x/cdp/keeper/seize_test.go @@ -34,6 +34,32 @@ type liquidationTracker struct { } func (suite *SeizeTestSuite) SetupTest() { + tApp := app.NewTestApp() + ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) + coins := []sdk.Coins{} + tracker := liquidationTracker{} + + for j := 0; j < 100; j++ { + coins = append(coins, cs(c("btc", 100000000), c("xrp", 10000000000))) + } + _, addrs := app.GeneratePrivKeyAddressPairs(100) + + authGS := app.NewAuthGenState( + addrs, coins) + tApp.InitializeFromGenesisStates( + authGS, + NewPricefeedGenStateMulti(), + NewCDPGenStateMulti(), + ) + suite.ctx = ctx + suite.app = tApp + suite.keeper = tApp.GetCDPKeeper() + suite.cdps = types.CDPs{} + suite.addrs = addrs + suite.liquidations = tracker +} + +func (suite *SeizeTestSuite) createCdps() { tApp := app.NewTestApp() ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) cdps := make(types.CDPs, 100) @@ -99,6 +125,7 @@ func (suite *SeizeTestSuite) setPrice(price sdk.Dec, market string) { } func (suite *SeizeTestSuite) TestSeizeCollateral() { + suite.createCdps() sk := suite.app.GetSupplyKeeper() cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2)) p := cdp.Principal[0].Amount @@ -118,6 +145,7 @@ func (suite *SeizeTestSuite) TestSeizeCollateral() { } func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { + suite.createCdps() sk := suite.app.GetSupplyKeeper() cdp, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(2)) err := suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], cs(c("xrp", 6999000000))) @@ -142,6 +170,7 @@ func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { } func (suite *SeizeTestSuite) TestLiquidateCdps() { + suite.createCdps() sk := suite.app.GetSupplyKeeper() acc := sk.GetModuleAccount(suite.ctx, types.ModuleName) originalXrpCollateral := acc.GetCoins().AmountOf("xrp") @@ -156,6 +185,7 @@ func (suite *SeizeTestSuite) TestLiquidateCdps() { } func (suite *SeizeTestSuite) TestHandleNewDebt() { + suite.createCdps() tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx") suite.keeper.HandleNewDebt(suite.ctx, "xrp", "usdx", i(31536000)) tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")