Skip to content

Commit

Permalink
Abstract errors (#1804)
Browse files Browse the repository at this point in the history
* Add concurrency to liquidated vault validation

* Abstract more errors from mn checks

* Restore full sync reconsiderblock to unstuck node

---------

Co-authored-by: jouzo <[email protected]>
Co-authored-by: Jouzo <[email protected]>
  • Loading branch information
3 people authored Mar 7, 2023
1 parent a34f178 commit 2f15bf7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 45 deletions.
1 change: 0 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,6 @@ libdefi_common_a_SOURCES = \
rpc/rawtransaction_util.cpp \
rpc/util.cpp \
rpc/stats.cpp \
rpc/resultcache.cpp \
scheduler.cpp \
script/descriptor.cpp \
script/sign.cpp \
Expand Down
44 changes: 28 additions & 16 deletions src/masternodes/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class DeFiErrors {
return Res::Err("Attributes unavailable");
}

static Res TokenInvalid(const std::string &token) {
return Res::Err("Cannot find token %s", token);
static Res TokenInvalidForName(const std::string &tokenName) {
return Res::Err("Cannot find token %s", tokenName);
}

static Res LoanPaybackWithCollateralDisable() {
Expand All @@ -47,12 +47,12 @@ class DeFiErrors {
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 LoanInvalidVault(const CVaultId &vault) {
return Res::Err("There are no loans on this vault (%s)!", vault.GetHex());
}

static Res LoanInvalidToken(const std::string &token) {
return Res::Err("There is no loan on token (%s) in this vault!", token);
static Res LoanInvalidTokenForSymbol(const std::string &symbol) {
return Res::Err("There is no loan on token (%s) in this vault!", symbol);
}

static Res VaultNoLoans(const std::string &token = "") {
Expand All @@ -77,15 +77,19 @@ class DeFiErrors {
schemeRatio);
}

static Res LoanTokenInvalid(const std::string &token) {
return Res::Err("Loan token %s does not exist!", token);
static Res LoanTokenNotFoundForName(const std::string &tokenName) {
return Res::Err("Loan token %s does not exist!", tokenName);
}

static Res VaultInvalid(const std::string &vaultId) {
return Res::Err("Cannot find existing vault with id %s", vaultId);
static Res VaultInvalid(const CVaultId vaultId) {
return Res::Err("Cannot find existing vault with id %s", vaultId.GetHex());
}

static Res VaultUnderLiquidation() {
return Res::Err("Vault is under liquidation");
}

static Res LoanNoPaybackOnLiquidation() {
return Res::Err("Cannot payback loan on vault under liquidation");
}

Expand All @@ -97,24 +101,32 @@ class DeFiErrors {
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 LoanTokenIdInvalid(const DCT_ID &tokenId) {
return Res::Err("Loan token with id (%s) does not exist!", tokenId.ToString());
}

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 TokenIdInvalid(const DCT_ID &tokenId) {
return Res::Err("Token with id (%s) does not exists", tokenId.ToString());
}

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);
static Res OracleNoLivePrice(const std::string &tokenSymbol, const std::string &currency) {
return Res::Err("No live fixed prices for %s/%s", tokenSymbol, currency);
}

static Res OracleNegativePrice(const std::string &tokenSymbol, const std::string &currency) {
return Res::Err("Negative price (%s/%s)", tokenSymbol, currency);
}

static Res AmountOverflowAsValuePrice(const CAmount amount, const CAmount price) {
return Res::Err("Value/price too high (%s/%s)", GetDecimaleString(amount), GetDecimaleString(price));
}
};

Expand Down
46 changes: 28 additions & 18 deletions src/masternodes/masternodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <masternodes/errors.h>

std::unique_ptr<CCustomCSView> pcustomcsview;
std::unique_ptr<CStorageLevelDB> pcustomcsDB;
Expand Down Expand Up @@ -993,16 +994,15 @@ ResVal<CAmount> CCustomCSView::GetAmountInCurrency(CAmount amount,
bool useNextPrice,
bool requireLivePrice) {
auto priceResult = GetValidatedIntervalPrice(priceFeedId, useNextPrice, requireLivePrice);
Require(priceResult);
if (!priceResult) return priceResult;

auto price = *priceResult.val;
auto amountInCurrency = MultiplyAmounts(price, amount);
if (price > COIN)
Require(amountInCurrency >= amount,
[=]{ return strprintf("Value/price too high (%s/%s)",
GetDecimaleString(amount),
GetDecimaleString(price)); });

if (price > COIN) {
if (amountInCurrency < amount) {
return DeFiErrors::AmountOverflowAsValuePrice(amount, price);
}
}
return {amountInCurrency, Res::Ok()};
}

Expand All @@ -1013,18 +1013,25 @@ ResVal<CCollateralLoans> CCustomCSView::GetLoanCollaterals(const CVaultId &vault
bool useNextPrice,
bool requireLivePrice) {
const auto vault = GetVault(vaultId);
Require(vault && !vault->isUnderLiquidation, []{ return "Vault is under liquidation";} );
if (!vault)
return DeFiErrors::VaultInvalid(vaultId);
if (vault->isUnderLiquidation)
return DeFiErrors::VaultUnderLiquidation();

CCollateralLoans result{};
Require(PopulateLoansData(result, vaultId, height, blockTime, useNextPrice, requireLivePrice));
Require(PopulateCollateralData(result, vaultId, collaterals, height, blockTime, useNextPrice, requireLivePrice));

LogPrint(BCLog::LOAN,
"%s(): totalCollaterals - %lld, totalLoans - %lld, ratio - %d\n",
if (auto res = PopulateLoansData(result, vaultId, height, blockTime, useNextPrice, requireLivePrice); !res)
return res;
if (auto res = PopulateCollateralData(result, vaultId, collaterals, height, blockTime, useNextPrice, requireLivePrice); !res)
return res;

if (LogAcceptCategory((BCLog::LOAN))) {
LogPrintf("%s(): totalCollaterals - %lld, totalLoans - %lld, ratio - %d\n",
__func__,
result.totalCollaterals,
result.totalLoans,
result.ratio());
}

return {result, Res::Ok()};
}
Expand All @@ -1036,14 +1043,18 @@ ResVal<CAmount> CCustomCSView::GetValidatedIntervalPrice(const CTokenCurrencyPai
auto currency = priceFeedId.second;

auto priceFeed = GetFixedIntervalPrice(priceFeedId);
Require(priceFeed);
if (!priceFeed) return priceFeed;

if (requireLivePrice)
Require(priceFeed->isLive(GetPriceDeviation()), [=]{ return strprintf("No live fixed prices for %s/%s", tokenSymbol, currency); });
if (requireLivePrice && !priceFeed->isLive(GetPriceDeviation())) {
return DeFiErrors::OracleNoLivePrice(tokenSymbol, currency);
}

auto priceRecordIndex = useNextPrice ? 1 : 0;
auto price = priceFeed.val->priceRecord[priceRecordIndex];
Require(price > 0, [=]{ return strprintf("Negative price (%s/%s)", tokenSymbol, currency); });

if (price <= 0) {
return DeFiErrors::OracleNegativePrice(tokenSymbol, currency);
}

return {price, Res::Ok()};
}
Expand All @@ -1055,8 +1066,7 @@ Res CCustomCSView::PopulateLoansData(CCollateralLoans &result,
bool useNextPrice,
bool requireLivePrice) {
const auto loanTokens = GetLoanTokens(vaultId);
if (!loanTokens)
return Res::Ok();
if (!loanTokens) return Res::Ok();

for (const auto &[loanTokenId, loanTokenAmount] : loanTokens->balances) {
const auto token = GetLoanTokenByID(loanTokenId);
Expand Down
22 changes: 12 additions & 10 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3326,7 +3326,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
CBalances *loan;
if (id == DCT_ID{0}) {
auto tokenDUSD = mnview.GetToken("DUSD");
if (!tokenDUSD) return DeFiErrors::LoanTokenInvalid("DUSD");
if (!tokenDUSD) return DeFiErrors::LoanTokenNotFoundForName("DUSD");
loan = &loans[tokenDUSD->first];
} else
loan = &loans[id];
Expand All @@ -3342,9 +3342,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
return res;

const auto vault = mnview.GetVault(obj.vaultId);
if (!vault) return DeFiErrors::VaultInvalid(obj.vaultId.GetHex());
if (!vault) return DeFiErrors::VaultInvalid(obj.vaultId);

if (vault->isUnderLiquidation) return DeFiErrors::VaultUnderLiquidation();
if (vault->isUnderLiquidation) return DeFiErrors::LoanNoPaybackOnLiquidation();

if (!mnview.GetVaultCollaterals(obj.vaultId)) return DeFiErrors::VaultNoCollateral(obj.vaultId.GetHex());

Expand All @@ -3366,7 +3366,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {

for (const auto &[loanTokenId, paybackAmounts] : obj.loans) {
const auto loanToken = mnview.GetLoanTokenByID(loanTokenId);
if (!loanToken) return DeFiErrors::LoanTokenIdInvalid(loanTokenId.ToString());
if (!loanToken) return DeFiErrors::LoanTokenIdInvalid(loanTokenId);

for (const auto &kv : paybackAmounts.balances) {
const auto &paybackTokenId = kv.first;
Expand All @@ -3379,7 +3379,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
CAmount paybackUsdPrice{0}, loanUsdPrice{0}, penaltyPct{COIN};

auto paybackToken = mnview.GetToken(paybackTokenId);
if (!paybackToken) return DeFiErrors::TokenIdInvalid(paybackTokenId.ToString());
if (!paybackToken) return DeFiErrors::TokenIdInvalid(paybackTokenId);

if (loanTokenId != paybackTokenId) {
if (!IsVaultPriceValid(mnview, obj.vaultId, height)) return DeFiErrors::LoanAssetPriceInvalid();
Expand Down Expand Up @@ -3418,8 +3418,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
if (loanToken->symbol == "DUSD") {
paybackAmount = usdAmount;
if (paybackUsdPrice > COIN) {
if (paybackAmount < kv.second) return DeFiErrors::LoanPriceInvalid(
GetDecimaleString(kv.second), GetDecimaleString(paybackUsdPrice));
if (paybackAmount < kv.second) {
return DeFiErrors::AmountOverflowAsValuePrice(kv.second, paybackUsdPrice);
}
}
} else {
// Get dToken price in USD
Expand All @@ -3437,9 +3438,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
}

const auto loanAmounts = mnview.GetLoanTokens(obj.vaultId);
if (!loanAmounts) return DeFiErrors::LoanInvalidVault(obj.vaultId.GetHex());
if (!loanAmounts) return DeFiErrors::LoanInvalidVault(obj.vaultId);

if (!loanAmounts->balances.count(loanTokenId)) return DeFiErrors::LoanInvalidToken(loanToken->symbol);
if (!loanAmounts->balances.count(loanTokenId))
return DeFiErrors::LoanInvalidTokenForSymbol(loanToken->symbol);

const auto &currentLoanAmount = loanAmounts->balances.at(loanTokenId);

Expand Down Expand Up @@ -4647,7 +4649,7 @@ Res PaybackWithCollateral(CCustomCSView &view,
if (!attributes) return DeFiErrors::MNInvalidAttribute();

const auto dUsdToken = view.GetToken("DUSD");
if (!dUsdToken) return DeFiErrors::TokenInvalid("DUSD");
if (!dUsdToken) return DeFiErrors::TokenInvalidForName("DUSD");

CDataStructureV0 activeKey{AttributeTypes::Token, dUsdToken->first.v, TokenKeys::LoanPaybackCollateral};
if (!attributes->GetValue(activeKey, false)) return DeFiErrors::LoanPaybackWithCollateralDisable();
Expand Down

0 comments on commit 2f15bf7

Please sign in to comment.