From aebeeb61b76992f8368c8d5ef18c75a5a09dc3f1 Mon Sep 17 00:00:00 2001 From: DrPing Date: Mon, 20 Feb 2023 12:05:52 +0800 Subject: [PATCH] Revert Require to if (#1769) * Replace Require by if for PaybackWithCollateral * Replace Require by if for Payback * Add prefix for error method * Change error prefix and merge some duplicate * Return Res from single Require args. Minor error string correction. --------- Co-authored-by: Bushstar --- src/masternodes/errors.h | 90 ++++++++++++++++++++ src/masternodes/mn_checks.cpp | 149 +++++++++++++++++++--------------- 2 files changed, 172 insertions(+), 67 deletions(-) diff --git a/src/masternodes/errors.h b/src/masternodes/errors.h index a4a2d958f7..b64c5908b9 100644 --- a/src/masternodes/errors.h +++ b/src/masternodes/errors.h @@ -26,6 +26,96 @@ class DeFiErrors { // TODO: Change error in later version to include amount. Retaining old msg for compatibility return Res::Err("Below minimum swapable amount, must be at least %s BTC", GetDecimaleString(minSwap)); } + + static Res MNInvalidAttribute() { + return Res::Err("Attributes unavailable"); + } + + static Res TokenInvalid(const std::string &token) { + return Res::Err("Cannot find token %s", token); + } + + static Res LoanPaybackWithCollateralDisable() { + return Res::Err("Payback of DUSD loan with collateral is not currently active"); + } + + static Res VaultNoCollateral(const std::string &vaultId = "") { + return vaultId.empty() ? Res::Err("Vault has no collaterals") : Res::Err("Vault with id %s has no collaterals", vaultId); + } + + static Res VaultNoDUSDCollateral() { + return Res::Err("Vault does not have any DUSD collaterals"); + } + + static Res LoanInvalidVault(const std::string &vault) { + return Res::Err("There are no loans on this vault (%s)!", vault); + } + + static Res LoanInvalidToken(const std::string &token) { + return Res::Err("There is no loan on token (%s) in this vault!", token); + } + + static Res VaultNoLoans(const std::string &token = "") { + return token.empty() ? Res::Err("Vault has no loans") : Res::Err("Vault does not have any %s loans", token); + } + + static Res TokenInterestRateInvalid(const std::string token) { + return Res::Err("Cannot get interest rate for this token (%s)!", token); + } + + static Res VaultNeedCollateral() { + return Res::Err("Vault cannot have loans without collaterals"); + } + + static Res VaultInvalidPrice() { + return Res::Err("Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); + } + + static Res VaultInsufficientCollateralization(const uint32_t collateralizationRatio, const uint32_t schemeRatio) { + return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", + collateralizationRatio, + schemeRatio); + } + + static Res LoanTokenInvalid(const std::string &token) { + return Res::Err("Loan token %s does not exist!", token); + } + + static Res VaultInvalid(const std::string &vaultId) { + return Res::Err("Cannot find existing vault with id %s", vaultId); + } + + static Res VaultUnderLiquidation() { + return Res::Err("Cannot payback loan on vault under liquidation"); + } + + static Res TXMissingInput() { + return Res::Err("tx must have at least one input from token owner"); + } + + static Res LoanAssetPriceInvalid() { + return Res::Err("Cannot payback loan while any of the asset's price is invalid"); + } + + static Res LoanTokenIdInvalid(const std::string &token) { + return Res::Err("Loan token with id (%s) does not exist!", token); + } + + static Res LoanPaymentAmountInvalid(const CAmount amount, const uint32_t value) { + return Res::Err("Valid payback amount required (input: %d@%d)", amount, value); + } + + static Res TokenIdInvalid(const std::string &token) { + return Res::Err("Token with id (%s) does not exists", token); + } + + static Res LoanPaybackDisabled(const std::string &token) { + return token.empty() ? Res::Err("Payback is not currently active") : Res::Err("Payback of loan via %s token is not currently active", token); + } + + static Res LoanPriceInvalid(const std::string &kv, const std::string &payback) { + return Res::Err("Value/price too high (%s/%s)", kv, payback); + } }; #endif // DEFI_MASTERNODES_ERRORS_H diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index f3ab16e3d8..6fc1ad4c09 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -3306,7 +3306,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CBalances *loan; if (id == DCT_ID{0}) { auto tokenDUSD = mnview.GetToken("DUSD"); - Require(tokenDUSD, "Loan token DUSD does not exist!"); + if (!tokenDUSD) return DeFiErrors::LoanTokenInvalid("DUSD"); loan = &loans[tokenDUSD->first]; } else loan = &loans[id]; @@ -3317,20 +3317,21 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CLoanPaybackLoanV2Message &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; const auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Cannot find existing vault with id %s", obj.vaultId.GetHex()); + if (!vault) return DeFiErrors::VaultInvalid(obj.vaultId.GetHex()); - Require(!vault->isUnderLiquidation, "Cannot payback loan on vault under liquidation"); + if (vault->isUnderLiquidation) return DeFiErrors::VaultUnderLiquidation(); - Require(mnview.GetVaultCollaterals(obj.vaultId), "Vault with id %s has no collaterals", obj.vaultId.GetHex()); + if (!mnview.GetVaultCollaterals(obj.vaultId)) return DeFiErrors::VaultNoCollateral(obj.vaultId.GetHex()); - Require(HasAuth(obj.from), "tx must have at least one input from token owner"); + if (!HasAuth(obj.from)) return DeFiErrors::TXMissingInput(); if (static_cast(height) < consensus.FortCanningRoadHeight) { - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot payback loan while any of the asset's price is invalid"); + if (!IsVaultPriceValid(mnview, obj.vaultId, height)) return DeFiErrors::LoanAssetPriceInvalid(); } // Handle payback with collateral special case @@ -3345,45 +3346,38 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (const auto &[loanTokenId, paybackAmounts] : obj.loans) { const auto loanToken = mnview.GetLoanTokenByID(loanTokenId); - Require(loanToken, "Loan token with id (%s) does not exist!", loanTokenId.ToString()); + if (!loanToken) return DeFiErrors::LoanTokenIdInvalid(loanTokenId.ToString()); for (const auto &kv : paybackAmounts.balances) { const auto &paybackTokenId = kv.first; auto paybackAmount = kv.second; if (height >= static_cast(consensus.FortCanningGreatWorldHeight)) { - Require(paybackAmount > 0, - "Valid payback amount required (input: %d@%d)", - paybackAmount, - paybackTokenId.v); + if (paybackAmount <= 0) return DeFiErrors::LoanPaymentAmountInvalid(paybackAmount, paybackTokenId.v); } CAmount paybackUsdPrice{0}, loanUsdPrice{0}, penaltyPct{COIN}; auto paybackToken = mnview.GetToken(paybackTokenId); - Require(paybackToken, "Token with id (%s) does not exists", paybackTokenId.ToString()); + if (!paybackToken) return DeFiErrors::TokenIdInvalid(paybackTokenId.ToString()); if (loanTokenId != paybackTokenId) { - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot payback loan while any of the asset's price is invalid"); - Require(attributes, "Payback is not currently active"); + if (!IsVaultPriceValid(mnview, obj.vaultId, height)) return DeFiErrors::LoanAssetPriceInvalid(); // search in token to token if (paybackTokenId != DCT_ID{0}) { CDataStructureV0 activeKey{ AttributeTypes::Token, loanTokenId.v, TokenKeys::LoanPayback, paybackTokenId.v}; - Require(attributes->GetValue(activeKey, false), - "Payback of loan via %s token is not currently active", - paybackToken->symbol); + if (!attributes->GetValue(activeKey, false)) return DeFiErrors::LoanPaybackDisabled( + paybackToken->symbol); CDataStructureV0 penaltyKey{ AttributeTypes::Token, loanTokenId.v, TokenKeys::LoanPaybackFeePCT, paybackTokenId.v}; penaltyPct -= attributes->GetValue(penaltyKey, CAmount{0}); } else { CDataStructureV0 activeKey{AttributeTypes::Token, loanTokenId.v, TokenKeys::PaybackDFI}; - Require(attributes->GetValue(activeKey, false), - "Payback of loan via %s token is not currently active", - paybackToken->symbol); + if (!attributes->GetValue(activeKey, false)) return DeFiErrors::LoanPaybackDisabled( + paybackToken->symbol); CDataStructureV0 penaltyKey{AttributeTypes::Token, loanTokenId.v, TokenKeys::PaybackDFIFeePCT}; penaltyPct -= attributes->GetValue(penaltyKey, COIN / 100); @@ -3393,7 +3387,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const CTokenCurrencyPair tokenUsdPair{paybackToken->symbol, "USD"}; bool useNextPrice{false}, requireLivePrice{true}; const auto resVal = mnview.GetValidatedIntervalPrice(tokenUsdPair, useNextPrice, requireLivePrice); - Require(resVal); + if (!resVal) + return std::move(resVal); paybackUsdPrice = MultiplyAmounts(*resVal.val, penaltyPct); @@ -3403,10 +3398,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (loanToken->symbol == "DUSD") { paybackAmount = usdAmount; if (paybackUsdPrice > COIN) { - Require(paybackAmount >= kv.second, - "Value/price too high (%s/%s)", - GetDecimaleString(kv.second), - GetDecimaleString(paybackUsdPrice)); + if (paybackAmount < kv.second) return DeFiErrors::LoanPriceInvalid( + GetDecimaleString(kv.second), GetDecimaleString(paybackUsdPrice)); } } else { // Get dToken price in USD @@ -3414,7 +3407,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { bool useNextPrice{false}, requireLivePrice{true}; const auto resVal = mnview.GetValidatedIntervalPrice(dTokenUsdPair, useNextPrice, requireLivePrice); - Require(resVal); + if (!resVal) + return std::move(resVal); loanUsdPrice = *resVal.val; @@ -3423,16 +3417,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } const auto loanAmounts = mnview.GetLoanTokens(obj.vaultId); - Require(loanAmounts, "There are no loans on this vault (%s)!", obj.vaultId.GetHex()); + if (!loanAmounts) return DeFiErrors::LoanInvalidVault(obj.vaultId.GetHex()); - Require(loanAmounts->balances.count(loanTokenId), - "There is no loan on token (%s) in this vault!", - loanToken->symbol); + if (!loanAmounts->balances.count(loanTokenId)) return DeFiErrors::LoanInvalidToken(loanToken->symbol); const auto ¤tLoanAmount = loanAmounts->balances.at(loanTokenId); const auto rate = mnview.GetInterestRate(obj.vaultId, loanTokenId, height); - Require(rate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); + if (!rate) return DeFiErrors::TokenInterestRateInvalid(loanToken->symbol); auto subInterest = TotalInterest(*rate, height); @@ -3456,12 +3448,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {loanTokenId, subLoan}); } - Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan})); + res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan}); + if (!res) + return res; // Eraseinterest. On subInterest is nil interest ITH and IPB will be updated, if // subInterest is negative or IPB is negative and subLoan is equal to the loan amount // then IPB will be updated and ITH will be wiped. - Require(mnview.DecreaseInterest( + res = mnview.DecreaseInterest( height, obj.vaultId, vault->schemeId, @@ -3469,12 +3463,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { subLoan, subInterest < 0 || (rate->interestPerBlock.negative && subLoan == currentLoanAmount) ? std::numeric_limits::max() - : subInterest)); + : subInterest); + if (!res) + return res; if (height >= static_cast(consensus.FortCanningMuseumHeight) && subLoan < currentLoanAmount && height < static_cast(consensus.FortCanningGreatWorldHeight)) { auto newRate = mnview.GetInterestRate(obj.vaultId, loanTokenId, height); - Require(newRate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); + if (!newRate) return DeFiErrors::TokenInterestRateInvalid(loanToken->symbol); Require(newRate->interestPerBlock.amount != 0, "Cannot payback this amount of loan for %s, either payback full amount or less than this " @@ -3485,7 +3481,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CalculateOwnerRewards(obj.from); if (paybackTokenId == loanTokenId) { - Require(mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest)); + res = mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest); + if (!res) + return res; // If interest was negative remove it from sub amount if (height >= static_cast(consensus.FortCanningEpilogueHeight) && subInterest < 0) @@ -3504,7 +3502,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { "CLoanPaybackLoanMessage(): Sub loan from balance - %lld, height - %d\n", subLoan, height); - Require(mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan})); + res = mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan}); + if (!res) + return res; } // burn interest Token->USD->DFI->burnAddress @@ -3514,8 +3514,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { loanToken->symbol, subInterest, height); - Require( - SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height)); + res = SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height); + if (!res) + return res; } } else { CAmount subInToken; @@ -3564,7 +3565,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { paybackToken->symbol, height); - Require(TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress)); + res = TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress); + if (!res) + return res; } else { CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackTokens}; auto balances = attributes->GetValue(liveKey, CTokenPayback{}); @@ -3586,13 +3589,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { AttributeTypes::Param, ParamIDs::DFIP2206A, DFIPKeys::DUSDLoanBurn}; auto directLoanBurn = attributes->GetValue(directBurnKey, false); - Require(SwapToDFIorDUSD(mnview, + res = SwapToDFIorDUSD(mnview, paybackTokenId, subInToken, obj.from, consensus.burnAddress, height, - !directLoanBurn)); + !directLoanBurn); + if (!res) + return res; } } } @@ -4619,30 +4624,30 @@ Res PaybackWithCollateral(CCustomCSView &view, uint32_t height, uint64_t time) { const auto attributes = view.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) return DeFiErrors::MNInvalidAttribute(); const auto dUsdToken = view.GetToken("DUSD"); - Require(dUsdToken, "Cannot find token DUSD"); + if (!dUsdToken) return DeFiErrors::TokenInvalid("DUSD"); CDataStructureV0 activeKey{AttributeTypes::Token, dUsdToken->first.v, TokenKeys::LoanPaybackCollateral}; - Require(attributes->GetValue(activeKey, false), "Payback of DUSD loan with collateral is not currently active"); + if (!attributes->GetValue(activeKey, false)) return DeFiErrors::LoanPaybackWithCollateralDisable(); const auto collateralAmounts = view.GetVaultCollaterals(vaultId); - Require(collateralAmounts, "Vault has no collaterals"); + if (!collateralAmounts) return DeFiErrors::VaultNoCollateral(); - Require(collateralAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD collaterals"); + if (!collateralAmounts->balances.count(dUsdToken->first)) return DeFiErrors::VaultNoDUSDCollateral(); const auto &collateralDUSD = collateralAmounts->balances.at(dUsdToken->first); const auto loanAmounts = view.GetLoanTokens(vaultId); - Require(loanAmounts, "Vault has no loans"); + if (!loanAmounts) return DeFiErrors::VaultNoLoans(); - Require(loanAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD loans"); + if (!loanAmounts->balances.count(dUsdToken->first)) return DeFiErrors::VaultNoLoans("DUSD"); const auto &loanDUSD = loanAmounts->balances.at(dUsdToken->first); const auto rate = view.GetInterestRate(vaultId, dUsdToken->first, height); - Require(rate, "Cannot get interest rate for this token (DUSD)!"); + if (!rate) return DeFiErrors::TokenInterestRateInvalid("DUSD"); const auto subInterest = TotalInterest(*rate, height); Res res{}; @@ -4654,9 +4659,13 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subInterest > collateralDUSD) { subCollateralAmount = collateralDUSD; - Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); + res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); + if (!res) + return res; - Require(view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount)); + res = view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount); + if (!res) + return res; burnAmount = subCollateralAmount; } else { @@ -4673,11 +4682,15 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subLoanAmount > 0) { TrackDUSDSub(view, {dUsdToken->first, subLoanAmount}); - Require(view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount})); + res = view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount}); + if (!res) + return res; } if (subCollateralAmount > 0) { - Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); + res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); + if (!res) + return res; } view.ResetInterest(height, vaultId, vault.schemeId, dUsdToken->first); @@ -4685,7 +4698,9 @@ Res PaybackWithCollateral(CCustomCSView &view, } if (burnAmount > 0) { - Require(view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount})); + res = view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount}); + if (!res) + return res; } else { TrackNegativeInterest(view, {dUsdToken->first, std::abs(burnAmount)}); } @@ -4694,25 +4709,25 @@ Res PaybackWithCollateral(CCustomCSView &view, const auto collaterals = view.GetVaultCollaterals(vaultId); const auto loans = view.GetLoanTokens(vaultId); if (loans) - Require(collaterals, "Vault cannot have loans without collaterals"); + if (!collaterals) return DeFiErrors::VaultNeedCollateral(); auto collateralsLoans = view.GetLoanCollaterals(vaultId, *collaterals, height, time); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); // The check is required to do a ratio check safe guard, or the vault of ratio is unreliable. // This can later be removed, if all edge cases of price deviations and max collateral factor for DUSD (1.5 // currently) can be tested for economical stability. Taking the safer approach for now. - Require(IsVaultPriceValid(view, vaultId, height), - "Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); + if (!IsVaultPriceValid(view, vaultId, height)) return DeFiErrors::VaultInvalidPrice(); const auto scheme = view.GetLoanScheme(vault.schemeId); - Require(collateralsLoans.val->ratio() >= scheme->ratio, - "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + if (collateralsLoans.val->ratio() < scheme->ratio) return DeFiErrors::VaultInsufficientCollateralization( + collateralsLoans.val->ratio(), scheme->ratio); if (subCollateralAmount > 0) { - Require(view.SubMintedTokens(dUsdToken->first, subCollateralAmount)); + res = view.SubMintedTokens(dUsdToken->first, subCollateralAmount); + if (!res) + return res; } return Res::Ok();