From 0f4589c4308688c477f09e68352d10c92565e0ff Mon Sep 17 00:00:00 2001 From: Peter John Bushnell Date: Sat, 24 Dec 2022 12:57:50 +0000 Subject: [PATCH] Refactor consensus with contract based predicates (#1644) (#1668) --- src/amount.h | 37 +- src/chainparams.cpp | 16 +- src/masternodes/accounts.cpp | 48 +- src/masternodes/balances.h | 34 +- src/masternodes/govvariables/attributes.cpp | 411 +--- .../govvariables/icx_takerfee_per_btc.cpp | 4 +- .../govvariables/loan_daily_reward.cpp | 4 +- .../govvariables/loan_liquidation_penalty.cpp | 7 +- src/masternodes/govvariables/loan_splits.cpp | 24 +- .../govvariables/lp_daily_dfi_reward.cpp | 4 +- src/masternodes/govvariables/lp_splits.cpp | 26 +- .../govvariables/oracle_block_interval.cpp | 10 +- .../govvariables/oracle_deviation.cpp | 7 +- src/masternodes/gv.cpp | 7 +- src/masternodes/icxorder.cpp | 86 +- src/masternodes/incentivefunding.cpp | 17 +- src/masternodes/loan.cpp | 56 +- src/masternodes/masternodes.cpp | 71 +- src/masternodes/mn_checks.cpp | 2145 ++++++----------- src/masternodes/mn_checks.h | 2 +- src/masternodes/oracles.cpp | 72 +- src/masternodes/poolpairs.cpp | 98 +- src/masternodes/res.h | 42 +- src/masternodes/rpc_oracles.cpp | 12 +- src/masternodes/rpc_poolpair.cpp | 1 + src/masternodes/tokens.cpp | 107 +- src/masternodes/tokens.h | 7 +- src/masternodes/vault.cpp | 19 +- src/rpc/rawtransaction_util.cpp | 4 +- src/test/util_tests.cpp | 19 + src/validation.cpp | 27 +- test/functional/feature_autoauth.py | 146 +- test/functional/feature_loan_scheme.py | 2 +- test/functional/feature_loan_vault.py | 4 +- test/functional/feature_tokens_multisig.py | 15 +- 35 files changed, 1290 insertions(+), 2301 deletions(-) diff --git a/src/amount.h b/src/amount.h index d00fd5a3812..c94f6edaff2 100644 --- a/src/amount.h +++ b/src/amount.h @@ -92,18 +92,16 @@ typedef std::map TAmounts; inline ResVal SafeAdd(CAmount _a, CAmount _b) { // check limits - if (_a < 0 || _b < 0) { - return Res::Err("negative amount"); - } + Require(_a >= 0 && _b >= 0, "negative amount"); + // convert to unsigned, because signed overflow is UB const uint64_t a = (uint64_t) _a; const uint64_t b = (uint64_t) _b; const uint64_t sum = a + b; // check overflow - if ((sum - a) != b || ((uint64_t)std::numeric_limits::max()) < sum) { - return Res::Err("overflow"); - } + + Require((sum - a) == b && (uint64_t)std::numeric_limits::max() >= sum, "overflow"); return {(CAmount) sum, Res::Ok()}; } @@ -127,29 +125,26 @@ struct CTokenAmount { // simple std::pair is less informative Res Add(CAmount amount) { // safety checks - if (amount < 0) { - return Res::Err("negative amount: %s", GetDecimaleString(amount)); - } + Require(amount >= 0, "negative amount: %s", GetDecimaleString(amount)); + // add - auto sumRes = SafeAdd(this->nValue, amount); - if (!sumRes.ok) { - return std::move(sumRes); - } - this->nValue = *sumRes.val; + auto sumRes = SafeAdd(nValue, amount); + Require(sumRes); + + nValue = *sumRes; return Res::Ok(); } + Res Sub(CAmount amount) { // safety checks - if (amount < 0) { - return Res::Err("negative amount: %s", GetDecimaleString(amount)); - } - if (this->nValue < amount) { - return Res::Err("amount %s is less than %s", GetDecimaleString(this->nValue), GetDecimaleString(amount)); - } + Require(amount >= 0, "negative amount: %s", GetDecimaleString(amount)); + Require(nValue >= amount, "amount %s is less than %s", GetDecimaleString(nValue), GetDecimaleString(amount)); + // sub - this->nValue -= amount; + nValue -= amount; return Res::Ok(); } + CAmount SubWithRemainder(CAmount amount) { // safety checks if (amount < 0) { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ac253dbfa99..6ed86a71a35 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1182,9 +1182,7 @@ void ClearCheckpoints(CChainParams ¶ms) { Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) { std::ifstream file(fileName); - if (!file.good()) { - return Res::Err("Could not read %s. Ensure it exists and has read permissions", fileName); - } + Require(file.good(), "Could not read %s. Ensure it exists and has read permissions", fileName); ClearCheckpoints(params); @@ -1196,19 +1194,13 @@ Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) std::istringstream iss(trimmed); std::string hashStr, heightStr; - if (!(iss >> heightStr >> hashStr)) { - return Res::Err("Error parsing line %s", trimmed); - } + Require((iss >> heightStr >> hashStr), "Error parsing line %s", trimmed); uint256 hash; - if (!ParseHashStr(hashStr, hash)) { - return Res::Err("Invalid hash: %s", hashStr); - } + Require(ParseHashStr(hashStr, hash), "Invalid hash: %s", hashStr); int32_t height; - if (!ParseInt32(heightStr, &height)) { - return Res::Err("Invalid height: %s", heightStr); - } + Require(ParseInt32(heightStr, &height), "Invalid height: %s", heightStr); params.checkpointData.mapCheckpoints[height] = hash; } diff --git a/src/masternodes/accounts.cpp b/src/masternodes/accounts.cpp index 0d36ca9d61b..df00f43a36b 100644 --- a/src/masternodes/accounts.cpp +++ b/src/masternodes/accounts.cpp @@ -36,10 +36,7 @@ Res CAccountsView::AddBalance(const CScript &owner, CTokenAmount amount) { return Res::Ok(); } auto balance = GetBalance(owner, amount.nTokenId); - auto res = balance.Add(amount.nValue); - if (!res.ok) { - return res; - } + Require(balance.Add(amount.nValue)); return SetBalance(owner, balance); } @@ -48,30 +45,21 @@ Res CAccountsView::SubBalance(const CScript &owner, CTokenAmount amount) { return Res::Ok(); } auto balance = GetBalance(owner, amount.nTokenId); - auto res = balance.Sub(amount.nValue); - if (!res.ok) { - return res; - } + Require(balance.Sub(amount.nValue)); return SetBalance(owner, balance); } Res CAccountsView::AddBalances(const CScript &owner, const CBalances &balances) { - for (const auto &kv : balances.balances) { - auto res = AddBalance(owner, CTokenAmount{kv.first, kv.second}); - if (!res.ok) { - return res; - } - } + for (const auto &kv : balances.balances) + Require(AddBalance(owner, CTokenAmount{kv.first, kv.second})); + return Res::Ok(); } Res CAccountsView::SubBalances(const CScript &owner, const CBalances &balances) { - for (const auto &kv : balances.balances) { - auto res = SubBalance(owner, CTokenAmount{kv.first, kv.second}); - if (!res.ok) { - return res; - } - } + for (const auto &kv : balances.balances) + Require(SubBalance(owner, CTokenAmount{kv.first, kv.second})); + return Res::Ok(); } @@ -92,10 +80,7 @@ uint32_t CAccountsView::GetBalancesHeight(const CScript &owner) { } Res CAccountsView::StoreFuturesUserValues(const CFuturesUserKey &key, const CFuturesUserValue &futures) { - if (!WriteBy(key, futures)) { - return Res::Err("Failed to store futures"); - } - + Require(WriteBy(key, futures), "Failed to store futures"); return Res::Ok(); } @@ -106,18 +91,12 @@ void CAccountsView::ForEachFuturesUserValues( } Res CAccountsView::EraseFuturesUserValues(const CFuturesUserKey &key) { - if (!EraseBy(key)) { - return Res::Err("Failed to erase futures"); - } - + Require(EraseBy(key), "Failed to erase futures"); return Res::Ok(); } Res CAccountsView::StoreFuturesDUSD(const CFuturesUserKey &key, const CAmount &amount) { - if (!WriteBy(key, amount)) { - return Res::Err("Failed to store futures"); - } - + Require(WriteBy(key, amount), "Failed to store futures"); return Res::Ok(); } @@ -127,9 +106,6 @@ void CAccountsView::ForEachFuturesDUSD(std::function(key)) { - return Res::Err("Failed to erase futures"); - } - + Require(EraseBy(key), "Failed to erase futures"); return Res::Ok(); } diff --git a/src/masternodes/balances.h b/src/masternodes/balances.h index 2182474a9b5..d369d092d2a 100644 --- a/src/masternodes/balances.h +++ b/src/masternodes/balances.h @@ -18,10 +18,7 @@ struct CBalances { return Res::Ok(); } auto current = CTokenAmount{amount.nTokenId, balances[amount.nTokenId]}; - auto res = current.Add(amount.nValue); - if (!res.ok) { - return res; - } + Require(current.Add(amount.nValue)); if (current.nValue == 0) { balances.erase(amount.nTokenId); } else { @@ -29,15 +26,14 @@ struct CBalances { } return Res::Ok(); } + Res Sub(CTokenAmount amount) { if (amount.nValue == 0) { return Res::Ok(); } auto current = CTokenAmount{amount.nTokenId, balances[amount.nTokenId]}; - auto res = current.Sub(amount.nValue); - if (!res.ok) { - return res; - } + Require(current.Sub(amount.nValue)); + if (current.nValue == 0) { balances.erase(amount.nTokenId); } else { @@ -45,6 +41,7 @@ struct CBalances { } return Res::Ok(); } + CTokenAmount SubWithRemainder(CTokenAmount amount) { if (amount.nValue == 0) { return CTokenAmount{amount.nTokenId, 0}; @@ -58,15 +55,14 @@ struct CBalances { } return CTokenAmount{amount.nTokenId, remainder}; } + Res SubBalances(const TAmounts &other) { - for (const auto &kv : other) { - auto res = Sub(CTokenAmount{kv.first, kv.second}); - if (!res.ok) { - return res; - } - } + for (const auto &[tokenId, amount] : other) + Require(Sub(CTokenAmount{tokenId, amount})); + return Res::Ok(); } + CBalances SubBalancesWithRemainder(const TAmounts &other) { CBalances remainderBalances; for (const auto &kv : other) { @@ -77,13 +73,11 @@ struct CBalances { } return remainderBalances; } + Res AddBalances(const TAmounts &other) { - for (const auto &kv : other) { - auto res = Add(CTokenAmount{kv.first, kv.second}); - if (!res.ok) { - return res; - } - } + for (const auto &[tokenId, amount] : other) + Require(Add(CTokenAmount{tokenId, amount})); + return Res::Ok(); } diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index 3d0b77ea0b3..fa4c5938336 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -308,17 +308,13 @@ const std::map> &ATTRIBUTES::displayKeys static ResVal VerifyInt32(const std::string &str) { int32_t int32; - if (!ParseInt32(str, &int32)) { - return Res::Err("Value must be an integer"); - } + Require(ParseInt32(str, &int32), "Value must be an integer"); return {int32, Res::Ok()}; } static ResVal VerifyPositiveInt32(const std::string &str) { int32_t int32; - if (!ParseInt32(str, &int32) || int32 < 0) { - return Res::Err("Value must be a positive integer"); - } + Require(ParseInt32(str, &int32) && int32 >= 0, "Value must be a positive integer"); return {int32, Res::Ok()}; } @@ -332,25 +328,19 @@ static ResVal VerifyUInt32(const std::string &str) { static ResVal VerifyInt64(const std::string &str) { CAmount int64; - if (!ParseInt64(str, &int64) || int64 < 0) { - return Res::Err("Value must be a positive integer"); - } + Require(ParseInt64(str, &int64) && int64 >= 0, "Value must be a positive integer"); return {int64, Res::Ok()}; } static ResVal VerifyFloat(const std::string &str) { CAmount amount = 0; - if (!ParseFixedPoint(str, 8, &amount)) { - return Res::Err("Amount must be a valid number"); - } + Require(ParseFixedPoint(str, 8, &amount), "Amount must be a valid number"); return {amount, Res::Ok()}; } ResVal VerifyPositiveFloat(const std::string &str) { CAmount amount = 0; - if (!ParseFixedPoint(str, 8, &amount) || amount < 0) { - return Res::Err("Amount must be a positive value"); - } + Require(ParseFixedPoint(str, 8, &amount) && amount >= 0, "Amount must be a positive value"); return {amount, Res::Ok()}; } @@ -391,20 +381,14 @@ static ResVal VerifyBool(const std::string &str) { static ResVal VerifySplit(const std::string &str) { OracleSplits splits; const auto pairs = KeyBreaker(str); - if (pairs.size() != 2) { - return Res::Err("Two int values expected for split in id/mutliplier"); - } + Require(pairs.size() == 2, "Two int values expected for split in id/mutliplier"); const auto resId = VerifyPositiveInt32(pairs[0]); - if (!resId) { - return resId; - } + Require(resId); + const auto resMultiplier = VerifyInt32(pairs[1]); - if (!resMultiplier) { - return resMultiplier; - } - if (*resMultiplier == 0) { - return Res::Err("Mutliplier cannot be zero"); - } + Require(resMultiplier); + Require(*resMultiplier != 0, "Mutliplier cannot be zero"); + splits[*resId] = *resMultiplier; return {splits, Res::Ok()}; @@ -450,14 +434,11 @@ static ResVal VerifyMember(const UniValue &array) { static ResVal VerifyCurrencyPair(const std::string &str) { const auto value = KeyBreaker(str); - if (value.size() != 2) { - return Res::Err("Exactly two entires expected for currency pair"); - } + Require(value.size() == 2, "Exactly two entires expected for currency pair"); + auto token = trim_all_ws(value[0]).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); auto currency = trim_all_ws(value[1]).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); - if (token.empty() || currency.empty()) { - return Res::Err("Empty token / currency"); - } + Require(!token.empty() && !currency.empty(), "Empty token / currency"); return { CTokenCurrencyPair{token, currency}, Res::Ok() @@ -469,9 +450,7 @@ static std::set dirSet{"both", "in", "out"}; static ResVal VerifyFeeDirection(const std::string &str) { auto lowerStr = ToLower(str); const auto it = dirSet.find(lowerStr); - if (it == dirSet.end()) { - return Res::Err("Fee direction value must be both, in or out"); - } + Require(it != dirSet.end(), "Fee direction value must be both, in or out"); return {CFeeDir{static_cast(std::distance(dirSet.begin(), it))}, Res::Ok()}; } @@ -628,12 +607,12 @@ ResVal GetFutureSwapContractAddress(const std::string &contract) { return {contractAddress, Res::Ok()}; } -static Res ShowError(const std::string &key, const std::map &keys) { +std::string ShowError(const std::string &key, const std::map &keys) { std::string error{"Unrecognised " + key + " argument provided, valid " + key + "s are:"}; for (const auto &pair : keys) { error += ' ' + pair.first + ','; } - return Res::Err(error); + return error; } static void TrackLiveBalance(CCustomCSView &mnview, @@ -686,66 +665,45 @@ void TrackLiveBalances(CCustomCSView &mnview, const CBalances &balances, const u Res ATTRIBUTES::ProcessVariable(const std::string &key, const std::optional &value, std::function applyVariable) { - if (key.size() > 128) { - return Res::Err("Identifier exceeds maximum length (128)"); - } + Require(key.size() <= 128, "Identifier exceeds maximum length (128)"); const auto keys = KeyBreaker(key); - if (keys.empty() || keys[0].empty()) { - return Res::Err("Empty version"); - } + Require(!keys.empty() && !keys[0].empty(), "Empty version"); - const auto &iver = allowedVersions().find(keys[0]); - if (iver == allowedVersions().end()) { - return Res::Err("Unsupported version"); - } + auto iver = allowedVersions().find(keys[0]); + Require(iver != allowedVersions().end(), "Unsupported version"); - const auto &version = iver->second; - if (version != VersionTypes::v0) { - return Res::Err("Unsupported version"); - } + auto version = iver->second; + Require(version == VersionTypes::v0, "Unsupported version"); - if (keys.size() < 4 || keys[1].empty() || keys[2].empty() || keys[3].empty()) { - return Res::Err("Incorrect key for . Object of ['//ID/','value'] expected"); - } + Require(keys.size() >= 4 && !keys[1].empty() && !keys[2].empty() && !keys[3].empty(), + "Incorrect key for . Object of ['//ID/','value'] expected"); auto itype = allowedTypes().find(keys[1]); - if (itype == allowedTypes().end()) { - return ::ShowError("type", allowedTypes()); - } + Require(itype != allowedTypes().end(), ::ShowError("type", allowedTypes())); const auto type = itype->second; uint32_t typeId{0}; if (type == AttributeTypes::Param) { auto id = allowedParamIDs().find(keys[2]); - if (id == allowedParamIDs().end()) { - return ::ShowError("param", allowedParamIDs()); - } + Require(id != allowedParamIDs().end(), ::ShowError("param", allowedParamIDs())); typeId = id->second; } else if (type == AttributeTypes::Locks) { auto id = allowedLocksIDs().find(keys[2]); - if (id == allowedLocksIDs().end()) { - return ::ShowError("locks", allowedLocksIDs()); - } + Require(id != allowedLocksIDs().end(), ::ShowError("locks", allowedLocksIDs())); typeId = id->second; } else if (type == AttributeTypes::Oracles) { auto id = allowedOracleIDs().find(keys[2]); - if (id == allowedOracleIDs().end()) { - return ::ShowError("oracles", allowedOracleIDs()); - } + Require(id != allowedOracleIDs().end(), ::ShowError("oracles", allowedOracleIDs())); typeId = id->second; } else if (type == AttributeTypes::Governance) { auto id = allowedGovernanceIDs().find(keys[2]); - if (id == allowedGovernanceIDs().end()) { - return ::ShowError("governance", allowedGovernanceIDs()); - } + Require(id != allowedGovernanceIDs().end(), ::ShowError("governance", allowedGovernanceIDs())); typeId = id->second; } else { auto id = VerifyInt32(keys[2]); - if (!id) { - return std::move(id); - } + Require(id); typeId = *id.val; } @@ -764,9 +722,7 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } } else { auto ikey = allowedKeys().find(type); - if (ikey == allowedKeys().end()) { - return Res::Err("Unsupported type {%d}", type); - } + Require(ikey != allowedKeys().end(), "Unsupported type {%d}", type); // Alias of reward_pct in Export. if (keys[3] == "fee_pct") { @@ -774,9 +730,7 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } itype = ikey->second.find(keys[3]); - if (itype == ikey->second.end()) { - return ::ShowError("key", ikey->second); - } + Require(itype != ikey->second.end(), ::ShowError("key", ikey->second)); typeKey = itype->second; @@ -799,10 +753,9 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } } } else if (typeId == ParamIDs::DFIP2206A) { - if (typeKey != DFIPKeys::DUSDInterestBurn && typeKey != DFIPKeys::DUSDLoanBurn) { - return Res::Err("Unsupported type for DFIP2206A {%d}", typeKey); - } - + Require(typeKey == DFIPKeys::DUSDInterestBurn || typeKey == DFIPKeys::DUSDLoanBurn, + "Unsupported type for DFIP2206A {%d}", + typeKey); } else if (typeId == ParamIDs::Feature) { if (typeKey != DFIPKeys::GovUnset && typeKey != DFIPKeys::GovFoundation && typeKey != DFIPKeys::MNSetRewardAddress && typeKey != DFIPKeys::MNSetOperatorAddress && @@ -836,18 +789,12 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } if (attrV0.IsExtendedSize()) { - if (keys.size() != 5 || keys[4].empty()) { - return Res::Err("Exact 5 keys are required {%d}", keys.size()); - } + Require(keys.size() == 5 && !keys[4].empty(), "Exact 5 keys are required {%d}", keys.size()); auto id = VerifyInt32(keys[4]); - if (!id) { - return std::move(id); - } + Require(id); attrV0.keyId = *id.val; } else { - if (keys.size() != 4) { - return Res::Err("Exact 4 keys are required {%d}", keys.size()); - } + Require(keys.size() == 4, "Exact 4 keys are required {%d}", keys.size()); } if (!value) { @@ -941,26 +888,17 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei CAccountsHistoryWriter subView( mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &subWriters); - auto res = subView.SubBalance(*contractAddressValue, value.source); - if (!res) { - return res; - } + Require(subView.SubBalance(*contractAddressValue, value.source)); subView.Flush(); CHistoryWriters addWriters{historyStore, nullptr, nullptr}; CAccountsHistoryWriter addView( mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &addWriters); - res = addView.AddBalance(key.owner, value.source); - if (!res) { - return res; - } + Require(addView.AddBalance(key.owner, value.source)); addView.Flush(); - res = balances.Sub(value.source); - if (!res) { - return res; - } + Require(balances.Sub(value.source)); } SetValue(liveKey, std::move(balances)); @@ -1013,10 +951,7 @@ Res ATTRIBUTES::RefundFuturesDUSD(CCustomCSView &mnview, const uint32_t height) } addView.Flush(); - res = balances.Sub({DCT_ID{}, amount}); - if (!res) { - return res; - } + Require(balances.Sub({DCT_ID{}, amount})); } SetValue(liveKey, std::move(balances)); @@ -1025,9 +960,7 @@ Res ATTRIBUTES::RefundFuturesDUSD(CCustomCSView &mnview, const uint32_t height) } Res ATTRIBUTES::Import(const UniValue &val) { - if (!val.isObject()) { - return Res::Err("Object of values expected"); - } + Require(val.isObject(), "Object of values expected"); std::map objMap; val.getObjMap(objMap); @@ -1099,7 +1032,6 @@ Res ATTRIBUTES::Import(const UniValue &val) { members[member.first] = member.second; } SetValue(*attrV0, members); - return Res::Ok(); } else return Res::Err("Invalid member data"); @@ -1110,7 +1042,7 @@ Res ATTRIBUTES::Import(const UniValue &val) { }); if (!res) { return res; - } + }; } return Res::Ok(); } @@ -1307,96 +1239,73 @@ UniValue ATTRIBUTES::Export() const { } Res ATTRIBUTES::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningHillHeight) - return Res::Err("Cannot be set before FortCanningHill"); + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHillHeight, + "Cannot be set before FortCanningHill"); for (const auto &[key, value] : attributes) { const auto attrV0 = std::get_if(&key); - if (!attrV0) { - return Res::Err("Unsupported version"); - } + Require(attrV0, "Unsupported version"); switch (attrV0->type) { case AttributeTypes::Token: switch (attrV0->key) { case TokenKeys::LoanPaybackCollateral: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningEpilogueHeight) { - return Res::Err("Cannot be set before FortCanningEpilogue"); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningEpilogueHeight, + "Cannot be set before FortCanningEpilogue"); [[fallthrough]]; case TokenKeys::PaybackDFI: case TokenKeys::PaybackDFIFeePCT: - if (!view.GetLoanTokenByID({attrV0->typeId})) { - return Res::Err("No such loan token (%d)", attrV0->typeId); - } + Require(view.GetLoanTokenByID({attrV0->typeId}), "No such loan token (%d)", attrV0->typeId); break; case TokenKeys::LoanPayback: case TokenKeys::LoanPaybackFeePCT: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningRoadHeight) { - return Res::Err("Cannot be set before FortCanningRoad"); - } - if (!view.GetLoanTokenByID(DCT_ID{attrV0->typeId})) { - return Res::Err("No such loan token (%d)", attrV0->typeId); - } - if (!view.GetToken(DCT_ID{attrV0->keyId})) { - return Res::Err("No such token (%d)", attrV0->keyId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, + "Cannot be set before FortCanningRoad"); + Require( + view.GetLoanTokenByID(DCT_ID{attrV0->typeId}), "No such loan token (%d)", attrV0->typeId); + Require(view.GetToken(DCT_ID{attrV0->keyId}), "No such token (%d)", attrV0->keyId); break; case TokenKeys::DexInFeePct: case TokenKeys::DexOutFeePct: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningRoadHeight) { - return Res::Err("Cannot be set before FortCanningRoad"); - } - if (!view.GetToken(DCT_ID{attrV0->typeId})) { - return Res::Err("No such token (%d)", attrV0->typeId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, + "Cannot be set before FortCanningRoad"); + Require(view.GetToken(DCT_ID{attrV0->typeId}), "No such token (%d)", attrV0->typeId); break; case TokenKeys::LoanCollateralFactor: if (view.GetLastHeight() < Params().GetConsensus().FortCanningEpilogueHeight) { const auto amount = std::get_if(&value); - if (amount && *amount > COIN) { - return Res::Err("Percentage exceeds 100%%"); - } + if (amount) + Require(*amount <= COIN, "Percentage exceeds 100%%"); } [[fallthrough]]; case TokenKeys::LoanMintingInterest: if (view.GetLastHeight() < Params().GetConsensus().FortCanningGreatWorldHeight) { const auto amount = std::get_if(&value); - if (amount && *amount < 0) { - return Res::Err("Amount must be a positive value"); - } + if (amount) + Require(*amount >= 0, "Amount must be a positive value"); } [[fallthrough]]; case TokenKeys::LoanCollateralEnabled: case TokenKeys::LoanMintingEnabled: { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningCrunchHeight) { - return Res::Err("Cannot be set before FortCanningCrunch"); - } - if (!VerifyToken(view, attrV0->typeId)) { - return Res::Err("No such token (%d)", attrV0->typeId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, + "Cannot be set before FortCanningCrunch"); + Require(VerifyToken(view, attrV0->typeId), "No such token (%d)", attrV0->typeId); CDataStructureV0 intervalPriceKey{ AttributeTypes::Token, attrV0->typeId, TokenKeys::FixedIntervalPriceId}; - if (GetValue(intervalPriceKey, CTokenCurrencyPair{}) == CTokenCurrencyPair{}) { - return Res::Err("Fixed interval price currency pair must be set first"); - } + Require(!(GetValue(intervalPriceKey, CTokenCurrencyPair{}) == CTokenCurrencyPair{}), + "Fixed interval price currency pair must be set first"); break; } case TokenKeys::FixedIntervalPriceId: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningCrunchHeight) { - return Res::Err("Cannot be set before FortCanningCrunch"); - } - if (!VerifyToken(view, attrV0->typeId)) { - return Res::Err("No such token (%d)", attrV0->typeId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, + "Cannot be set before FortCanningCrunch"); + Require(VerifyToken(view, attrV0->typeId), "No such token (%d)", attrV0->typeId); break; case TokenKeys::DFIP2203Enabled: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningRoadHeight) { - return Res::Err("Cannot be set before FortCanningRoad"); - } - if (!view.GetLoanTokenByID(DCT_ID{attrV0->typeId})) { - return Res::Err("No such loan token (%d)", attrV0->typeId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, + "Cannot be set before FortCanningRoad"); + Require( + view.GetLoanTokenByID(DCT_ID{attrV0->typeId}), "No such loan token (%d)", attrV0->typeId); break; case TokenKeys::Ascendant: case TokenKeys::Descendant: @@ -1454,31 +1363,20 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { break; case AttributeTypes::Oracles: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningCrunchHeight) { - return Res::Err("Cannot be set before FortCanningCrunch"); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, + "Cannot be set before FortCanningCrunch"); if (attrV0->typeId == OracleIDs::Splits) { const auto splitMap = std::get_if(&value); - if (!splitMap) { - return Res::Err("Unsupported value"); - } + Require(splitMap, "Unsupported value"); + for (const auto &[tokenId, multipler] : *splitMap) { - if (tokenId == 0) { - return Res::Err("Tokenised DFI cannot be split"); - } - if (view.HasPoolPair(DCT_ID{tokenId})) { - return Res::Err("Pool tokens cannot be split"); - } + Require(tokenId != 0, "Tokenised DFI cannot be split"); + Require(!view.HasPoolPair(DCT_ID{tokenId}), "Pool tokens cannot be split"); const auto token = view.GetToken(DCT_ID{tokenId}); - if (!token) { - return Res::Err("Token (%d) does not exist", tokenId); - } - if (!token->IsDAT()) { - return Res::Err("Only DATs can be split"); - } - if (!view.GetLoanTokenByID(DCT_ID{tokenId}).has_value()) { - return Res::Err("No loan token with id (%d)", tokenId); - } + Require(token, "Token (%d) does not exist", tokenId); + Require(token->IsDAT(), "Only DATs can be split"); + Require( + view.GetLoanTokenByID(DCT_ID{tokenId}).has_value(), "No loan token with id (%d)", tokenId); } } else { return Res::Err("Unsupported key"); @@ -1489,18 +1387,13 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { switch (attrV0->key) { case PoolKeys::TokenAFeePCT: case PoolKeys::TokenBFeePCT: - if (!view.GetPoolPair({attrV0->typeId})) { - return Res::Err("No such pool (%d)", attrV0->typeId); - } + Require(view.GetPoolPair({attrV0->typeId}), "No such pool (%d)", attrV0->typeId); break; case PoolKeys::TokenAFeeDir: case PoolKeys::TokenBFeeDir: - if (view.GetLastHeight() < Params().GetConsensus().FortCanningSpringHeight) { - return Res::Err("Cannot be set before FortCanningSpringHeight"); - } - if (!view.GetPoolPair({attrV0->typeId})) { - return Res::Err("No such pool (%d)", attrV0->typeId); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningSpringHeight, + "Cannot be set before FortCanningSpringHeight"); + Require(view.GetPoolPair({attrV0->typeId}), "No such pool (%d)", attrV0->typeId); break; default: return Res::Err("Unsupported key"); @@ -1527,9 +1420,8 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { return Res::Err("Cannot be set before FortCanningSpringHeight"); } } else if (attrV0->typeId == ParamIDs::DFIP2203) { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningRoadHeight) { - return Res::Err("Cannot be set before FortCanningRoadHeight"); - } + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, + "Cannot be set before FortCanningRoadHeight"); } else if (attrV0->typeId != ParamIDs::DFIP2201) { return Res::Err("Unrecognised param id"); } @@ -1575,15 +1467,11 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { if (attrV0->key == PoolKeys::TokenAFeePCT || attrV0->key == PoolKeys::TokenBFeePCT) { auto poolId = DCT_ID{attrV0->typeId}; auto pool = mnview.GetPoolPair(poolId); - if (!pool) { - return Res::Err("No such pool (%d)", poolId.v); - } + Require(pool, "No such pool (%d)", poolId.v); auto tokenId = attrV0->key == PoolKeys::TokenAFeePCT ? pool->idTokenA : pool->idTokenB; const auto valuePct = std::get_if(&attribute.second); - if (!valuePct) { - return Res::Err("Unexpected type"); - } + Require(valuePct, "Unexpected type"); if (auto res = mnview.SetDexFeePct(poolId, tokenId, *valuePct); !res) { return res; } @@ -1595,9 +1483,7 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { std::swap(tokenA, tokenB); } const auto valuePct = std::get_if(&attribute.second); - if (!valuePct) { - return Res::Err("Unexpected type"); - } + Require(valuePct, "Unexpected type"); if (auto res = mnview.SetDexFeePct(tokenA, tokenB, *valuePct); !res) { return res; } @@ -1621,27 +1507,20 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { if (aggregatePrice) { fixedIntervalPrice.priceRecord[1] = aggregatePrice; } - const auto res = mnview.SetFixedIntervalPrice(fixedIntervalPrice); - if (!res) { - return res; - } + Require(mnview.SetFixedIntervalPrice(fixedIntervalPrice)); } else { return Res::Err("Unrecognised value for FixedIntervalPriceId"); } } else if (attrV0->key == TokenKeys::DFIP2203Enabled) { const auto value = std::get_if(&attribute.second); - if (!value) { - return Res::Err("Unexpected type"); - } + Require(value, "Unexpected type"); if (*value) { continue; } const auto token = mnview.GetLoanTokenByID(DCT_ID{attrV0->typeId}); - if (!token) { - return Res::Err("No such loan token (%d)", attrV0->typeId); - } + Require(token, "No such loan token (%d)", attrV0->typeId); // Special case: DUSD will be used as a source for swaps but will // be set as disabled for Future swap destination. @@ -1649,17 +1528,12 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { continue; } - auto res = RefundFuturesContracts(mnview, height, attrV0->typeId); - if (!res) { - return res; - } + Require(RefundFuturesContracts(mnview, height, attrV0->typeId)); } else if (attrV0->key == TokenKeys::LoanMintingInterest) { if (height >= static_cast(Params().GetConsensus().FortCanningGreatWorldHeight) && interestTokens.count(attrV0->typeId)) { const auto tokenInterest = std::get_if(&attribute.second); - if (!tokenInterest) { - return Res::Err("Unexpected type"); - } + Require(tokenInterest, "Unexpected type"); std::set affectedVaults; mnview.ForEachLoanTokenAmount([&](const CVaultId &vaultId, const CBalances &balances) { @@ -1692,6 +1566,7 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { ratio.insert(data.ratio); return true; }); + // No loan schemes, fall back to 100% limit if (ratio.empty()) { if (const auto amount = std::get_if(&attribute.second); amount && *amount > COIN) { @@ -1699,13 +1574,10 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } } else { const auto factor = std::get_if(&attribute.second); - if (!factor) { - return Res::Err("Unexpected type"); - } - if (*factor >= *ratio.begin() * CENT) { - return Res::Err("Factor cannot be more than or equal to the lowest scheme rate of %d\n", - GetDecimaleString(*ratio.begin() * CENT)); - } + Require(factor, "Unexpected type"); + Require(*factor < *ratio.begin() * CENT, + "Factor cannot be more than or equal to the lowest scheme rate of %d\n", + GetDecimaleString(*ratio.begin() * CENT)); } } } @@ -1713,19 +1585,13 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { if (attrV0->typeId == ParamIDs::DFIP2203) { if (attrV0->key == DFIPKeys::Active) { const auto value = std::get_if(&attribute.second); - if (!value) { - return Res::Err("Unexpected type"); - } + Require(value, "Unexpected type"); if (*value) { continue; } - Res res = RefundFuturesContracts(mnview, height); - if (!res) { - return res; - } - + Require(RefundFuturesContracts(mnview, height)); } else if (attrV0->key == DFIPKeys::BlockPeriod || attrV0->key == DFIPKeys::StartBlock) { // Only check this when block period has been set, otherwise // it will fail when DFIP2203 active is set to true. @@ -1734,26 +1600,18 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2203, DFIPKeys::Active}; - if (GetValue(activeKey, false)) { - return Res::Err("Cannot set block period while DFIP2203 is active"); - } + Require(!GetValue(activeKey, false), "Cannot set block period while DFIP2203 is active"); } } else if (attrV0->typeId == ParamIDs::DFIP2206F) { if (attrV0->key == DFIPKeys::Active) { const auto value = std::get_if(&attribute.second); - if (!value) { - return Res::Err("Unexpected type"); - } + Require(value, "Unexpected type"); if (*value) { continue; } - Res res = RefundFuturesDUSD(mnview, height); - if (!res) { - return res; - } - + Require(RefundFuturesDUSD(mnview, height)); } else if (attrV0->key == DFIPKeys::BlockPeriod) { // Only check this when block period has been set, otherwise // it will fail when DFIP2206F active is set to true. @@ -1762,54 +1620,42 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2206F, DFIPKeys::Active}; - if (GetValue(activeKey, false)) { - return Res::Err("Cannot set block period while DFIP2206F is active"); - } + Require(!GetValue(activeKey, false), "Cannot set block period while DFIP2206F is active"); } } } else if (attrV0->type == AttributeTypes::Oracles && attrV0->typeId == OracleIDs::Splits) { const auto value = std::get_if(&attribute.second); - if (!value) { - return Res::Err("Unsupported value"); - } + Require(value, "Unsupported value"); for (const auto split : tokenSplits) { if (auto it{value->find(split)}; it == value->end()) { continue; } - if (attrV0->key <= height) { - return Res::Err("Cannot be set at or below current height"); - } + Require(attrV0->key > height, "Cannot be set at or below current height"); CDataStructureV0 lockKey{AttributeTypes::Locks, ParamIDs::TokenID, split}; if (GetValue(lockKey, false)) { continue; } - if (!mnview.GetLoanTokenByID(DCT_ID{split}).has_value()) { - return Res::Err("Auto lock. No loan token with id (%d)", split); - } + Require( + mnview.GetLoanTokenByID(DCT_ID{split}).has_value(), "Auto lock. No loan token with id (%d)", split); const auto startHeight = attrV0->key - Params().GetConsensus().blocksPerDay() / 2; if (height < startHeight) { auto var = GovVariable::Create("ATTRIBUTES"); - if (!var) { - return Res::Err("Failed to create Gov var for lock"); - } + Require(var, "Failed to create Gov var for lock"); + auto govVar = std::dynamic_pointer_cast(var); - if (!govVar) { - return Res::Err("Failed to cast Gov var to ATTRIBUTES"); - } + Require(govVar, "Failed to cast Gov var to ATTRIBUTES"); govVar->attributes[lockKey] = true; CGovernanceHeightMessage lock; lock.startHeight = startHeight; lock.govVar = govVar; - auto res = storeGovVars(lock, mnview); - if (!res) { - return res; - } + + Require(storeGovVars(lock, mnview)); } else { // Less than a day's worth of blocks, apply instant lock SetValue(lockKey, true); @@ -1822,23 +1668,17 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { Res ATTRIBUTES::Erase(CCustomCSView &mnview, uint32_t, const std::vector &keys) { for (const auto &key : keys) { - auto res = ProcessVariable(key, {}, [&](const CAttributeType &attribute, const CAttributeValue &) { + Require(ProcessVariable(key, {}, [&](const CAttributeType &attribute, const CAttributeValue &) { auto attrV0 = std::get_if(&attribute); if (!attrV0) { return Res::Ok(); } - if (attrV0->type == AttributeTypes::Live) { - return Res::Err("Live attribute cannot be deleted"); - } - if (!EraseKey(attribute)) { - return Res::Err("Attribute {%d} not exists", attrV0->type); - } + Require(attrV0->type != AttributeTypes::Live, "Live attribute cannot be deleted"); + Require(EraseKey(attribute), "Attribute {%d} not exists", attrV0->type); if (attrV0->type == AttributeTypes::Poolpairs) { auto poolId = DCT_ID{attrV0->typeId}; auto pool = mnview.GetPoolPair(poolId); - if (!pool) { - return Res::Err("No such pool (%d)", poolId.v); - } + Require(pool, "No such pool (%d)", poolId.v); auto tokenId = attrV0->key == PoolKeys::TokenAFeePCT ? pool->idTokenA : pool->idTokenB; return mnview.EraseDexFeePct(poolId, tokenId); @@ -1852,10 +1692,7 @@ Res ATTRIBUTES::Erase(CCustomCSView &mnview, uint32_t, const std::vector 0, "takerFeePerBTC cannot be 0 or less"); return Res::Ok(); } diff --git a/src/masternodes/govvariables/loan_daily_reward.cpp b/src/masternodes/govvariables/loan_daily_reward.cpp index 3315c1d6bf0..ce3889d97b9 100644 --- a/src/masternodes/govvariables/loan_daily_reward.cpp +++ b/src/masternodes/govvariables/loan_daily_reward.cpp @@ -22,9 +22,7 @@ UniValue LP_DAILY_LOAN_TOKEN_REWARD::Export() const { } Res LP_DAILY_LOAN_TOKEN_REWARD::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningHeight) - return Res::Err("Cannot be set before FortCanning"); - + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); return Res::Err("Cannot be set manually."); } diff --git a/src/masternodes/govvariables/loan_liquidation_penalty.cpp b/src/masternodes/govvariables/loan_liquidation_penalty.cpp index 1aa4bb904ff..abf78871c51 100644 --- a/src/masternodes/govvariables/loan_liquidation_penalty.cpp +++ b/src/masternodes/govvariables/loan_liquidation_penalty.cpp @@ -22,11 +22,8 @@ UniValue LOAN_LIQUIDATION_PENALTY::Export() const { } Res LOAN_LIQUIDATION_PENALTY::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningHeight) - return Res::Err("Cannot be set before FortCanning"); - - if (penalty < COIN / 100) - return Res::Err("Penalty cannot be less than 0.01 DFI"); + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); + Require(penalty >= COIN / 100, "Penalty cannot be less than 0.01 DFI"); return Res::Ok(); } diff --git a/src/masternodes/govvariables/loan_splits.cpp b/src/masternodes/govvariables/loan_splits.cpp index 0964316cdff..4fb6592bce6 100644 --- a/src/masternodes/govvariables/loan_splits.cpp +++ b/src/masternodes/govvariables/loan_splits.cpp @@ -13,13 +13,11 @@ bool LP_LOAN_TOKEN_SPLITS::IsEmpty() const { } Res LP_LOAN_TOKEN_SPLITS::Import(const UniValue &val) { - if (!val.isObject()) - return Res::Err("object of {poolId: rate,... } expected"); + Require(val.isObject(), "object of {poolId: rate,... } expected"); for (const std::string &key : val.getKeys()) { - const auto id = DCT_ID::FromString(key); - if (!id) - return std::move(id); + auto id = DCT_ID::FromString(key); + Require(id); splits.emplace(*id.val, AmountFromValue(val[key])); } return Res::Ok(); @@ -34,22 +32,20 @@ UniValue LP_LOAN_TOKEN_SPLITS::Export() const { } Res LP_LOAN_TOKEN_SPLITS::Validate(const CCustomCSView &mnview) const { - if (mnview.GetLastHeight() < Params().GetConsensus().FortCanningHeight) - return Res::Err("Cannot be set before FortCanning"); + Require(mnview.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); CAmount total{0}; for (const auto &kv : splits) { - if (!mnview.HasPoolPair(kv.first)) - return Res::Err("pool with id=%s not found", kv.first.ToString()); + Require(mnview.HasPoolPair(kv.first), "pool with id=%s not found", kv.first.ToString()); - if (kv.second < 0 || kv.second > COIN) - return Res::Err( - "wrong percentage for pool with id=%s, value = %s", kv.first.ToString(), std::to_string(kv.second)); + Require(kv.second >= 0 && kv.second <= COIN, + "wrong percentage for pool with id=%s, value = %s", + kv.first.ToString(), + std::to_string(kv.second)); total += kv.second; } - if (total != COIN) - return Res::Err("total = %d vs expected %d", total, COIN); + Require(total == COIN, "total = %d vs expected %d", total, COIN); return Res::Ok(); } diff --git a/src/masternodes/govvariables/lp_daily_dfi_reward.cpp b/src/masternodes/govvariables/lp_daily_dfi_reward.cpp index c3bbf28c7df..955e864aefd 100644 --- a/src/masternodes/govvariables/lp_daily_dfi_reward.cpp +++ b/src/masternodes/govvariables/lp_daily_dfi_reward.cpp @@ -22,9 +22,7 @@ UniValue LP_DAILY_DFI_REWARD::Export() const { } Res LP_DAILY_DFI_REWARD::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() >= Params().GetConsensus().EunosHeight) - return Res::Err("Cannot be set manually after Eunos hard fork"); - + Require(view.GetLastHeight() < Params().GetConsensus().EunosHeight, "Cannot be set manually after Eunos hard fork"); // nothing to do return Res::Ok(); } diff --git a/src/masternodes/govvariables/lp_splits.cpp b/src/masternodes/govvariables/lp_splits.cpp index 7e56a9eea31..1b113ecc61e 100644 --- a/src/masternodes/govvariables/lp_splits.cpp +++ b/src/masternodes/govvariables/lp_splits.cpp @@ -13,13 +13,12 @@ bool LP_SPLITS::IsEmpty() const { } Res LP_SPLITS::Import(const UniValue &val) { - if (!val.isObject()) - return Res::Err("object of {poolId: rate,... } expected"); /// throw here? cause "AmountFromValue" can throw! + Require(val.isObject(), + "object of {poolId: rate,... } expected"); /// throw here? cause "AmountFromValue" can throw! for (const std::string &key : val.getKeys()) { - const auto id = DCT_ID::FromString(key); - if (!id) - return std::move(id); + auto id = DCT_ID::FromString(key); + Require(id); splits.emplace(*id.val, AmountFromValue(val[key])); // todo: AmountFromValue } return Res::Ok(); @@ -35,18 +34,17 @@ UniValue LP_SPLITS::Export() const { Res LP_SPLITS::Validate(const CCustomCSView &mnview) const { CAmount total{0}; - for (const auto &kv : splits) { - if (!mnview.HasPoolPair(kv.first)) - return Res::Err("pool with id=%s not found", kv.first.ToString()); + for (const auto &[poolId, amount] : splits) { + Require(mnview.HasPoolPair(poolId), "pool with id=%s not found", poolId.ToString()); - if (kv.second < 0 || kv.second > COIN) - return Res::Err( - "wrong percentage for pool with id=%s, value = %s", kv.first.ToString(), std::to_string(kv.second)); + Require(amount >= 0 && amount <= COIN, + "wrong percentage for pool with id=%s, value = %s", + poolId.ToString(), + std::to_string(amount)); - total += kv.second; + total += amount; } - if (total != COIN) - return Res::Err("total = %d vs expected %d", total, COIN); + Require(total == COIN, "total = %d vs expected %d", total, COIN); return Res::Ok(); } diff --git a/src/masternodes/govvariables/oracle_block_interval.cpp b/src/masternodes/govvariables/oracle_block_interval.cpp index 30b436b74e3..eef339da81f 100644 --- a/src/masternodes/govvariables/oracle_block_interval.cpp +++ b/src/masternodes/govvariables/oracle_block_interval.cpp @@ -13,8 +13,7 @@ bool ORACLE_BLOCK_INTERVAL::IsEmpty() const { } Res ORACLE_BLOCK_INTERVAL::Import(const UniValue &val) { - if (!val.isNum()) - throw JSONRPCError(RPC_TYPE_ERROR, "Block interval amount is not a number"); + Require(val.isNum(), "Block interval amount is not a number"); blockInterval = val.get_int(); return Res::Ok(); @@ -25,11 +24,8 @@ UniValue ORACLE_BLOCK_INTERVAL::Export() const { } Res ORACLE_BLOCK_INTERVAL::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningHeight) - return Res::Err("Cannot be set before FortCanning"); - - if (blockInterval <= 0) - return Res::Err("Block interval cannot be less than 1"); + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); + Require(blockInterval > 0, "Block interval cannot be less than 1"); return Res::Ok(); } diff --git a/src/masternodes/govvariables/oracle_deviation.cpp b/src/masternodes/govvariables/oracle_deviation.cpp index bf35bbc530d..cf28b8a2159 100644 --- a/src/masternodes/govvariables/oracle_deviation.cpp +++ b/src/masternodes/govvariables/oracle_deviation.cpp @@ -22,11 +22,8 @@ UniValue ORACLE_DEVIATION::Export() const { } Res ORACLE_DEVIATION::Validate(const CCustomCSView &view) const { - if (view.GetLastHeight() < Params().GetConsensus().FortCanningHeight) - return Res::Err("Cannot be set before FortCanning"); - - if (deviation < COIN / 100) - return Res::Err("Deviation cannot be less than 1 percent"); + Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); + Require(deviation >= COIN / 100, "Deviation cannot be less than 1 percent"); return Res::Ok(); } diff --git a/src/masternodes/gv.cpp b/src/masternodes/gv.cpp index 23feec956fe..b91af7126ca 100644 --- a/src/masternodes/gv.cpp +++ b/src/masternodes/gv.cpp @@ -55,11 +55,8 @@ std::shared_ptr CGovView::GetVariable(const std::string &name) cons } Res CGovView::SetStoredVariables(const std::set> &govVars, const uint32_t height) { - for (auto &item : govVars) { - if (!WriteBy(GovVarKey{height, item->GetName()}, *item)) { - return Res::Err("Cannot write to DB"); - } - } + for (auto &item : govVars) + Require(WriteBy(GovVarKey{height, item->GetName()}, *item), "Cannot write to DB"); return Res::Ok(); } diff --git a/src/masternodes/icxorder.cpp b/src/masternodes/icxorder.cpp index 5bad1e5bc5f..c33af2fe9a6 100644 --- a/src/masternodes/icxorder.cpp +++ b/src/masternodes/icxorder.cpp @@ -64,18 +64,17 @@ uint8_t CICXOrderView::GetICXOrderStatus(const OrderKey &key) const { Res CICXOrderView::ICXCreateOrder(const CICXOrderImpl &order) { // this should not happen, but for sure - if (GetICXOrderByCreationTx(order.creationTx)) - return Res::Err("order with creation tx %s already exists!", order.creationTx.GetHex()); - if (order.orderType != CICXOrder::TYPE_INTERNAL && order.orderType != CICXOrder::TYPE_EXTERNAL) - return Res::Err("invalid order type!"); - if (order.amountFrom == 0) - return Res::Err("order amountFrom must be greater than 0!"); - if (order.amountToFill != order.amountFrom) - return Res::Err("order amountToFill is not equal to amountFrom!"); - if (order.orderPrice == 0) - return Res::Err("order price must be greater than 0!"); - if (order.expiry < CICXOrder::DEFAULT_EXPIRY) - return Res::Err("order expiry must be greater than %d!", CICXOrder::DEFAULT_EXPIRY - 1); + Require(!GetICXOrderByCreationTx(order.creationTx), + "order with creation tx %s already exists!", + order.creationTx.GetHex()); + Require(order.orderType == CICXOrder::TYPE_INTERNAL || order.orderType == CICXOrder::TYPE_EXTERNAL, + "invalid order type!"); + Require(order.amountFrom != 0, "order amountFrom must be greater than 0!"); + Require(order.amountToFill == order.amountFrom, "order amountToFill does not equal to amountFrom!"); + Require(order.orderPrice != 0, "order price must be greater than 0!"); + Require(order.expiry >= CICXOrder::DEFAULT_EXPIRY, + "order expiry must be greater than %d!", + CICXOrder::DEFAULT_EXPIRY - 1); OrderKey key(order.idToken, order.creationTx); WriteBy(order.creationTx, order); @@ -88,8 +87,9 @@ Res CICXOrderView::ICXCreateOrder(const CICXOrderImpl &order) { Res CICXOrderView::ICXUpdateOrder(const CICXOrderImpl &order) { // this should not happen, but for sure - if (!GetICXOrderByCreationTx(order.creationTx)) - return Res::Err("order with creation tx %s doesn't exists!", order.creationTx.GetHex()); + Require(GetICXOrderByCreationTx(order.creationTx), + "order with creation tx %s doesn't exists!", + order.creationTx.GetHex()); OrderKey key(order.idToken, order.creationTx); WriteBy(order.creationTx, order); @@ -150,10 +150,10 @@ uint8_t CICXOrderView::GetICXMakeOfferStatus(const TxidPairKey &key) const { Res CICXOrderView::ICXMakeOffer(const CICXMakeOfferImpl &makeoffer) { // this should not happen, but for sure - if (GetICXMakeOfferByCreationTx(makeoffer.creationTx)) - return Res::Err("makeoffer with creation tx %s already exists!", makeoffer.creationTx.GetHex()); - if (makeoffer.amount == 0) - return Res::Err("offer amount must be greater than 0!"); + Require(!GetICXMakeOfferByCreationTx(makeoffer.creationTx), + "makeoffer with creation tx %s already exists!", + makeoffer.creationTx.GetHex()); + Require(makeoffer.amount != 0, "offer amount must be greater than 0!"); WriteBy(makeoffer.creationTx, makeoffer); WriteBy(TxidPairKey(makeoffer.orderTx, makeoffer.creationTx), CICXMakeOffer::STATUS_OPEN); @@ -211,14 +211,12 @@ std::unique_ptr CICXOrderView::GetICXSubmi Res CICXOrderView::ICXSubmitDFCHTLC(const CICXSubmitDFCHTLCImpl &submitdfchtlc) { // this should not happen, but for sure - if (GetICXSubmitDFCHTLCByCreationTx(submitdfchtlc.creationTx)) - return Res::Err("submitdfchtlc with creation tx %s already exists!", submitdfchtlc.creationTx.GetHex()); - if (submitdfchtlc.amount == 0) - return Res::Err("Invalid amount, must be greater than 0!"); - if (submitdfchtlc.hash.IsNull()) - return Res::Err("Invalid hash, htlc hash is empty and it must be set!"); - if (submitdfchtlc.timeout == 0) - return Res::Err("Invalid timeout, must be greater than 0!"); + Require(!GetICXSubmitDFCHTLCByCreationTx(submitdfchtlc.creationTx), + "submitdfchtlc with creation tx %s already exists!", + submitdfchtlc.creationTx.GetHex()); + Require(submitdfchtlc.amount != 0, "Invalid amount, must be greater than 0!"); + Require(!submitdfchtlc.hash.IsNull(), "Invalid hash, htlc hash is empty and it must be set!"); + Require(submitdfchtlc.timeout != 0, "Invalid timeout, must be greater than 0!"); WriteBy(submitdfchtlc.creationTx, submitdfchtlc); WriteBy(TxidPairKey(submitdfchtlc.offerTx, submitdfchtlc.creationTx), @@ -292,18 +290,15 @@ std::unique_ptr CICXOrderView::GetICXSubmi Res CICXOrderView::ICXSubmitEXTHTLC(const CICXSubmitEXTHTLCImpl &submitexthtlc) { // this should not happen, but for sure - if (GetICXSubmitEXTHTLCByCreationTx(submitexthtlc.creationTx)) - return Res::Err("submitexthtlc with creation tx %s already exists!", submitexthtlc.creationTx.GetHex()); - if (submitexthtlc.amount == 0) - return Res::Err("Invalid amount, must be greater than 0!"); - if (submitexthtlc.htlcscriptAddress.empty()) - return Res::Err("Invalid htlcscriptAddress, htlcscriptAddress is empty and it must be set!"); - if (submitexthtlc.hash.IsNull()) - return Res::Err("Invalid hash, htlc hash is empty and it must be set!"); - if (!submitexthtlc.ownerPubkey.IsFullyValid()) - return Res::Err("Invalid refundPubkey is not a valid pubkey!"); - if (submitexthtlc.timeout == 0) - return Res::Err("Invalid timout, must be greater than 0!"); + Require(!GetICXSubmitEXTHTLCByCreationTx(submitexthtlc.creationTx), + "submitexthtlc with creation tx %s already exists!", + submitexthtlc.creationTx.GetHex()); + Require(submitexthtlc.amount != 0, "Invalid amount, must be greater than 0!"); + Require(!submitexthtlc.htlcscriptAddress.empty(), + "Invalid htlcscriptAddress, htlcscriptAddress is empty and it must be set!"); + Require(!submitexthtlc.hash.IsNull(), "Invalid hash, htlc hash is empty and it must be set!"); + Require(submitexthtlc.ownerPubkey.IsFullyValid(), "Invalid refundPubkey is not a valid pubkey!"); + Require(submitexthtlc.timeout != 0, "Invalid timout, must be greater than 0!"); WriteBy(submitexthtlc.creationTx, submitexthtlc); WriteBy(TxidPairKey(submitexthtlc.offerTx, submitexthtlc.creationTx), @@ -372,8 +367,9 @@ Res CICXOrderView::ICXClaimDFCHTLC(const CICXClaimDFCHTLCImpl &claimdfchtlc, const uint256 &offertxid, const CICXOrderImpl &order) { // this should not happen, but for sure - if (GetICXClaimDFCHTLCByCreationTx(claimdfchtlc.creationTx)) - return Res::Err("claimdfchtlc with creation tx %s already exists!", claimdfchtlc.creationTx.GetHex()); + Require(!GetICXClaimDFCHTLCByCreationTx(claimdfchtlc.creationTx), + "claimdfchtlc with creation tx %s already exists!", + claimdfchtlc.creationTx.GetHex()); WriteBy(claimdfchtlc.creationTx, claimdfchtlc); WriteBy(TxidPairKey(offertxid, claimdfchtlc.creationTx), CICXSubmitDFCHTLC::STATUS_CLAIMED); @@ -399,8 +395,9 @@ std::unique_ptr CICXOrderView::GetICXCloseOrd Res CICXOrderView::ICXCloseOrder(const CICXCloseOrderImpl &closeorder) { // this should not happen, but for sure - if (GetICXCloseOrderByCreationTx(closeorder.creationTx)) - return Res::Err("closeorder with creation tx %s already exists!", closeorder.creationTx.GetHex()); + Require(!GetICXCloseOrderByCreationTx(closeorder.creationTx), + "closeorder with creation tx %s already exists!", + closeorder.creationTx.GetHex()); WriteBy(closeorder.creationTx, closeorder.orderTx); @@ -417,8 +414,9 @@ std::unique_ptr CICXOrderView::GetICXCloseOff Res CICXOrderView::ICXCloseOffer(const CICXCloseOfferImpl &closeoffer) { // this should not happen, but for sure - if (GetICXCloseOrderByCreationTx(closeoffer.creationTx)) - return Res::Err("closeooffer with creation tx %s already exists!", closeoffer.creationTx.GetHex()); + Require(!GetICXCloseOrderByCreationTx(closeoffer.creationTx), + "closeooffer with creation tx %s already exists!", + closeoffer.creationTx.GetHex()); WriteBy(closeoffer.creationTx, closeoffer.offerTx); diff --git a/src/masternodes/incentivefunding.cpp b/src/masternodes/incentivefunding.cpp index 463927fb596..99705bc4dd5 100644 --- a/src/masternodes/incentivefunding.cpp +++ b/src/masternodes/incentivefunding.cpp @@ -15,9 +15,7 @@ CAmount CCommunityBalancesView::GetCommunityBalance(CommunityAccountType account Res CCommunityBalancesView::SetCommunityBalance(CommunityAccountType account, CAmount amount) { // deny negative values on db level! - if (amount < 0) { - return Res::Err("negative amount"); - } + Require(amount >= 0, "negative amount"); WriteBy(static_cast(account), amount); return Res::Ok(); } @@ -35,20 +33,17 @@ Res CCommunityBalancesView::AddCommunityBalance(CommunityAccountType account, CA if (amount == 0) { return Res::Ok(); } - auto res = SafeAdd(amount, GetCommunityBalance(account)); - return !res.ok ? res : SetCommunityBalance(account, *res.val); + auto sum = SafeAdd(amount, GetCommunityBalance(account)); + Require(sum); + return SetCommunityBalance(account, sum); } Res CCommunityBalancesView::SubCommunityBalance(CommunityAccountType account, CAmount amount) { if (amount == 0) { return Res::Ok(); } - if (amount < 0) { - return Res::Err("negative amount"); - } + Require(amount > 0, "negative amount"); CAmount oldBalance = GetCommunityBalance(account); - if (oldBalance < amount) { - return Res::Err("Amount %d is less than %d", oldBalance, amount); - } + Require(oldBalance >= amount, "Amount %d is less than %d", oldBalance, amount); return SetCommunityBalance(account, oldBalance - amount); } diff --git a/src/masternodes/loan.cpp b/src/masternodes/loan.cpp index d01d49b1385..beca57c2b22 100644 --- a/src/masternodes/loan.cpp +++ b/src/masternodes/loan.cpp @@ -12,12 +12,12 @@ std::optional CLoanView::GetLoanCollater Res CLoanView::CreateLoanCollateralToken(const CLoanSetCollateralTokenImpl &collToken) { // this should not happen, but for sure - if (GetLoanCollateralToken(collToken.creationTx)) - return Res::Err("setCollateralToken with creation tx %s already exists!", collToken.creationTx.GetHex()); - if (collToken.factor > COIN) - return Res::Err("setCollateralToken factor must be lower or equal than %s!", GetDecimaleString(COIN)); - if (collToken.factor < 0) - return Res::Err("setCollateralToken factor must not be negative!"); + Require(!GetLoanCollateralToken(collToken.creationTx), + "setCollateralToken with creation tx %s already exists!", + collToken.creationTx.GetHex()); + Require( + collToken.factor <= COIN, "setCollateralToken factor must be lower or equal than %s!", GetDecimaleString(COIN)); + Require(collToken.factor >= 0, "setCollateralToken factor must not be negative!"); WriteBy(collToken.creationTx, collToken); @@ -57,8 +57,7 @@ std::optional CLoanView::GetLoanToken(const ui Res CLoanView::SetLoanToken(const CLoanSetLoanTokenImpl &loanToken, DCT_ID const &id) { // this should not happen, but for sure - if (GetLoanTokenByID(id)) - return Res::Err("setLoanToken with creation tx %s already exists!", loanToken.creationTx.GetHex()); + Require(!GetLoanTokenByID(id), "setLoanToken with creation tx %s already exists!", loanToken.creationTx.GetHex()); WriteBy(id, loanToken); WriteBy(loanToken.creationTx, id); @@ -123,9 +122,8 @@ Res CLoanView::StoreDefaultLoanScheme(const std::string &loanSchemeID) { std::optional CLoanView::GetDefaultLoanScheme() { std::string loanSchemeID; - if (Read(DefaultLoanSchemeKey::prefix(), loanSchemeID)) { + if (Read(DefaultLoanSchemeKey::prefix(), loanSchemeID)) return loanSchemeID; - } return {}; } @@ -287,19 +285,16 @@ Res CLoanView::IncreaseInterest(const uint32_t height, const CAmount tokenInterest, const CAmount loanIncreased) { const auto scheme = GetLoanScheme(loanSchemeID); - if (!scheme) - return Res::Err("No such scheme id %s", loanSchemeID); + Require(scheme, "No such scheme id %s", loanSchemeID); - const auto token = GetLoanTokenByID(id); - if (!token) - return Res::Err("No such loan token id %s", id.ToString()); + auto token = GetLoanTokenByID(id); + Require(token, "No such loan token id %s", id.ToString()); CInterestRateV3 rate{}; if (auto readRate = GetInterestRate(vaultId, id, height)) rate = *readRate; - if (rate.height > height || height == 0) - return Res::Err("Cannot store height in the past"); + Require(height >= rate.height, "Cannot store height in the past"); rate.interestToHeight = TotalInterestCalculation(rate, height); rate.height = height; @@ -337,23 +332,19 @@ Res CLoanView::DecreaseInterest(const uint32_t height, const DCT_ID id, const CAmount loanDecreased, const CAmount interestDecreased) { - auto scheme = GetLoanScheme(loanSchemeID); - if (!scheme) - return Res::Err("No such scheme id %s", loanSchemeID); + const auto scheme = GetLoanScheme(loanSchemeID); + Require(scheme, "No such scheme id %s", loanSchemeID); auto token = GetLoanTokenByID(id); - if (!token) - return Res::Err("No such loan token id %s", id.ToString()); + Require(token, "No such loan token id %s", id.ToString()); CInterestRateV3 rate{}; if (auto readRate = GetInterestRate(vaultId, id, height)) rate = *readRate; - if (rate.height > height) - return Res::Err("Cannot store height in the past"); + Require(height >= rate.height, "Cannot store height in the past"); - if (rate.height == 0) - return Res::Err("Data mismatch height == 0"); + Require(rate.height != 0, "Data mismatch height == 0"); const auto interestToHeight = TotalInterestCalculation(rate, height); const auto interestDecreasedHP = ToHigherPrecision(interestDecreased, height); @@ -535,14 +526,11 @@ void CLoanView::MigrateInterestRateToV3(CVaultView &view, uint32_t height) { } Res CLoanView::AddLoanToken(const CVaultId &vaultId, CTokenAmount amount) { - if (!GetLoanTokenByID(amount.nTokenId)) - return Res::Err("No such loan token id %s", amount.nTokenId.ToString()); + Require(GetLoanTokenByID(amount.nTokenId), "No such loan token id %s", amount.nTokenId.ToString()); CBalances amounts; ReadBy(vaultId, amounts); - auto res = amounts.Add(amount); - if (!res) - return res; + Require(amounts.Add(amount)); if (!amounts.balances.empty()) WriteBy(vaultId, amounts); @@ -551,12 +539,10 @@ Res CLoanView::AddLoanToken(const CVaultId &vaultId, CTokenAmount amount) { } Res CLoanView::SubLoanToken(const CVaultId &vaultId, CTokenAmount amount) { - if (!GetLoanTokenByID(amount.nTokenId)) - return Res::Err("No such loan token id %s", amount.nTokenId.ToString()); + Require(GetLoanTokenByID(amount.nTokenId), "No such loan token id %s", amount.nTokenId.ToString()); auto amounts = GetLoanTokens(vaultId); - if (!amounts || !amounts->Sub(amount)) - return Res::Err("Loan token for vault <%s> not found", vaultId.GetHex()); + Require(amounts && amounts->Sub(amount), "Loan token for vault <%s> not found", vaultId.GetHex()); if (amounts->balances.empty()) EraseBy(vaultId); diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index 3c27a250d25..1973612a4bd 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -86,8 +86,7 @@ CAmount GetPropsCreationFee(int, const CCustomCSView &view, const CCreatePropMes CAmount cfpFee; switch (type) { - case CPropType::CommunityFundProposal: - { + case CPropType::CommunityFundProposal: { cfpFee = MultiplyAmounts(msg.nAmount, attributes->GetValue(CFPKey, Params().GetConsensus().props.cfp.fee)); auto minimumFee = Params().GetConsensus().props.cfp.minimumFee; return minimumFee > cfpFee ? minimumFee : cfpFee; @@ -321,17 +320,12 @@ Res CMasternodesView::CreateMasternode(const uint256 &nodeId, const CMasternode Res CMasternodesView::ResignMasternode(CMasternode &node, const uint256 &nodeId, const uint256 &txid, int height) { auto state = node.GetState(height, *this); if (height >= Params().GetConsensus().EunosPayaHeight) { - if (state != CMasternode::ENABLED) { - return Res::Err("node %s state is not 'ENABLED'", nodeId.ToString()); - } + Require(state == CMasternode::ENABLED, "node %s state is not 'ENABLED'", nodeId.ToString()); } else if ((state != CMasternode::PRE_ENABLED && state != CMasternode::ENABLED)) { return Res::Err("node %s state is not 'PRE_ENABLED' or 'ENABLED'", nodeId.ToString()); } - const auto timelock = GetTimelock(nodeId, node, height); - if (timelock) { - return Res::Err("Trying to resign masternode before timelock expiration."); - } + Require(!GetTimelock(nodeId, node, height), "Trying to resign masternode before timelock expiration."); node.resignTx = txid; node.resignHeight = height; @@ -457,18 +451,16 @@ std::optional CMasternodesView::GetMasternodeLastBlockTime(const CKeyID ForEachMinterNode( [&](const MNBlockTimeKey &key, int64_t blockTime) { - if (key.masternodeID == nodeId) { + if (key.masternodeID == nodeId) time = blockTime; - } // Get first result only and exit return false; }, MNBlockTimeKey{*nodeId, height - 1}); - if (time) { + if (time) return time; - } return {}; } @@ -976,13 +968,15 @@ ResVal CCustomCSView::GetAmountInCurrency(CAmount amount, bool useNextPrice, bool requireLivePrice) { auto priceResult = GetValidatedIntervalPrice(priceFeedId, useNextPrice, requireLivePrice); - if (!priceResult) - return priceResult; + Require(priceResult); auto price = *priceResult.val; auto amountInCurrency = MultiplyAmounts(price, amount); - if (price > COIN && amountInCurrency < amount) - return Res::Err("Value/price too high (%s/%s)", GetDecimaleString(amount), GetDecimaleString(price)); + if (price > COIN) + Require(amountInCurrency >= amount, + "Value/price too high (%s/%s)", + GetDecimaleString(amount), + GetDecimaleString(price)); return {amountInCurrency, Res::Ok()}; } @@ -994,17 +988,11 @@ ResVal CCustomCSView::GetLoanCollaterals(const CVaultId &vault bool useNextPrice, bool requireLivePrice) { const auto vault = GetVault(vaultId); - if (!vault || vault->isUnderLiquidation) - return Res::Err("Vault is under liquidation"); + Require(vault && !vault->isUnderLiquidation, "Vault is under liquidation"); CCollateralLoans result{}; - auto res = PopulateLoansData(result, vaultId, height, blockTime, useNextPrice, requireLivePrice); - if (!res) - return std::move(res); - - res = PopulateCollateralData(result, vaultId, collaterals, height, blockTime, useNextPrice, requireLivePrice); - if (!res) - return std::move(res); + 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", @@ -1023,16 +1011,14 @@ ResVal CCustomCSView::GetValidatedIntervalPrice(const CTokenCurrencyPai auto currency = priceFeedId.second; auto priceFeed = GetFixedIntervalPrice(priceFeedId); - if (!priceFeed) - return std::move(priceFeed); + Require(priceFeed); - if (requireLivePrice && !priceFeed.val->isLive(GetPriceDeviation())) - return Res::Err("No live fixed prices for %s/%s", tokenSymbol, currency); + if (requireLivePrice) + Require(priceFeed->isLive(GetPriceDeviation()), "No live fixed prices for %s/%s", tokenSymbol, currency); auto priceRecordIndex = useNextPrice ? 1 : 0; auto price = priceFeed.val->priceRecord[priceRecordIndex]; - if (price <= 0) - return Res::Err("Negative price (%s/%s)", tokenSymbol, currency); + Require(price > 0, "Negative price (%s/%s)", tokenSymbol, currency); return {price, Res::Ok()}; } @@ -1049,15 +1035,12 @@ Res CCustomCSView::PopulateLoansData(CCollateralLoans &result, for (const auto &[loanTokenId, loanTokenAmount] : loanTokens->balances) { const auto token = GetLoanTokenByID(loanTokenId); - if (!token) - return Res::Err("Loan token with id (%s) does not exist!", loanTokenId.ToString()); + Require(token, "Loan token with id (%s) does not exist!", loanTokenId.ToString()); const auto rate = GetInterestRate(vaultId, loanTokenId, height); - if (!rate) - return Res::Err("Cannot get interest rate for token (%s)!", token->symbol); + Require(rate, "Cannot get interest rate for token (%s)!", token->symbol); - if (rate->height > height) - return Res::Err("Trying to read loans in the past"); + Require(height >= rate->height, "Trying to read loans in the past"); auto totalAmount = loanTokenAmount + TotalInterest(*rate, height); if (totalAmount < 0) { @@ -1071,8 +1054,7 @@ Res CCustomCSView::PopulateLoansData(CCollateralLoans &result, auto prevLoans = result.totalLoans; result.totalLoans += *amountInCurrency.val; - if (prevLoans > result.totalLoans) - return Res::Err("Exceeded maximum loans"); + Require(prevLoans <= result.totalLoans, "Exceeded maximum loans"); result.loans.push_back({loanTokenId, amountInCurrency}); } @@ -1091,21 +1073,18 @@ Res CCustomCSView::PopulateCollateralData(CCollateralLoans &result, auto tokenAmount = col.second; auto token = HasLoanCollateralToken({tokenId, height}); - if (!token) - return Res::Err("Collateral token with id (%s) does not exist!", tokenId.ToString()); + Require(token, "Collateral token with id (%s) does not exist!", tokenId.ToString()); auto amountInCurrency = GetAmountInCurrency(tokenAmount, token->fixedIntervalPriceId, useNextPrice, requireLivePrice); - if (!amountInCurrency) - return std::move(amountInCurrency); + Require(amountInCurrency); auto amountFactor = MultiplyAmounts(token->factor, *amountInCurrency.val); auto prevCollaterals = result.totalCollaterals; result.totalCollaterals += amountFactor; - if (prevCollaterals > result.totalCollaterals) - return Res::Err("Exceeded maximum collateral"); + Require(prevCollaterals <= result.totalCollaterals, "Exceeded maximum collateral"); result.collaterals.push_back({tokenId, amountInCurrency}); } diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index a3b87643d0a..6e96c4ca3d8 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -162,10 +162,7 @@ static ResVal BurntTokens(const CTransaction &tx) { CBalances balances; for (const auto &out : tx.vout) { if (out.scriptPubKey.size() > 0 && out.scriptPubKey[0] == OP_RETURN) { - auto res = balances.Add(out.TokenAmount()); - if (!res.ok) { - return res; - } + Require(balances.Add(out.TokenAmount())); } } return {balances, Res::Ok()}; @@ -174,10 +171,7 @@ static ResVal BurntTokens(const CTransaction &tx) { static ResVal MintedTokens(const CTransaction &tx, uint32_t mintingOutputsStart) { CBalances balances; for (uint32_t i = mintingOutputsStart; i < (uint32_t)tx.vout.size(); i++) { - auto res = balances.Add(tx.vout[i].TokenAmount()); - if (!res.ok) { - return res; - } + Require(balances.Add(tx.vout[i].TokenAmount())); } return {balances, Res::Ok()}; } @@ -316,58 +310,43 @@ class CCustomMetadataParseVisitor { const std::vector &metadata; Res isPostAMKFork() const { - if (static_cast(height) < consensus.AMKHeight) { - return Res::Err("called before AMK height"); - } + Require(static_cast(height) >= consensus.AMKHeight, "called before AMK height"); return Res::Ok(); } Res isPostBayfrontFork() const { - if (static_cast(height) < consensus.BayfrontHeight) { - return Res::Err("called before Bayfront height"); - } + Require(static_cast(height) >= consensus.BayfrontHeight, "called before Bayfront height"); return Res::Ok(); } Res isPostBayfrontGardensFork() const { - if (static_cast(height) < consensus.BayfrontGardensHeight) { - return Res::Err("called before Bayfront Gardens height"); - } + Require(static_cast(height) >= consensus.BayfrontGardensHeight, "called before Bayfront Gardens height"); return Res::Ok(); } Res isPostEunosFork() const { - if (static_cast(height) < consensus.EunosHeight) { - return Res::Err("called before Eunos height"); - } + Require(static_cast(height) >= consensus.EunosHeight, "called before Eunos height"); return Res::Ok(); } Res isPostFortCanningFork() const { - if (static_cast(height) < consensus.FortCanningHeight) { - return Res::Err("called before FortCanning height"); - } + Require(static_cast(height) >= consensus.FortCanningHeight, "called before FortCanning height"); return Res::Ok(); } Res isPostFortCanningHillFork() const { - if (static_cast(height) < consensus.FortCanningHillHeight) { - return Res::Err("called before FortCanningHill height"); - } + Require(static_cast(height) >= consensus.FortCanningHillHeight, "called before FortCanningHill height"); return Res::Ok(); } Res isPostFortCanningRoadFork() const { - if (static_cast(height) < consensus.FortCanningRoadHeight) { - return Res::Err("called before FortCanningRoad height"); - } + Require(static_cast(height) >= consensus.FortCanningRoadHeight, "called before FortCanningRoad height"); return Res::Ok(); } Res isPostFortCanningEpilogueFork() const { - if (static_cast(height) < consensus.FortCanningEpilogueHeight) { - return Res::Err("called before FortCanningEpilogue height"); - } + Require(static_cast(height) >= consensus.FortCanningEpilogueHeight, + "called before FortCanningEpilogue height"); return Res::Ok(); } @@ -382,9 +361,7 @@ class CCustomMetadataParseVisitor { Res serialize(T &obj) const { CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); ss >> obj; - if (!ss.empty()) { - return Res::Err("deserialization failed: excess %d bytes", ss.size()); - } + Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); return Res::Ok(); } @@ -399,93 +376,83 @@ class CCustomMetadataParseVisitor { Res operator()(CCreateMasterNodeMessage &obj) const { return serialize(obj); } Res operator()(CResignMasterNodeMessage &obj) const { - if (metadata.size() != sizeof(obj)) { - return Res::Err("metadata must contain 32 bytes"); - } + Require(metadata.size() == sizeof(obj), "metadata must contain 32 bytes"); return serialize(obj); } Res operator()(CUpdateMasterNodeMessage &obj) const { - auto res = isPostGrandCentralFork(); - return !res ? res : serialize(obj); + Require(isPostGrandCentralFork()); + return serialize(obj); } Res operator()(CCreateTokenMessage &obj) const { - auto res = isPostAMKFork(); - return !res ? res : serialize(obj); + Require(isPostAMKFork()); + return serialize(obj); } Res operator()(CUpdateTokenPreAMKMessage &obj) const { - auto res = isPostAMKFork(); - if (!res) { - return res; - } - if (isPostBayfrontFork()) { - return Res::Err("called post Bayfront height"); - } + Require(isPostAMKFork()); + Require(!isPostBayfrontFork(), "called post Bayfront height"); return serialize(obj); } Res operator()(CUpdateTokenMessage &obj) const { - auto res = isPostBayfrontFork(); - return !res ? res : serialize(obj); + Require(isPostBayfrontFork()); + return serialize(obj); } Res operator()(CMintTokensMessage &obj) const { - auto res = isPostAMKFork(); - return !res ? res : serialize(obj); + Require(isPostAMKFork()); + return serialize(obj); } Res operator()(CPoolSwapMessage &obj) const { - auto res = isPostBayfrontFork(); - return !res ? res : serialize(obj); + Require(isPostBayfrontFork()); + return serialize(obj); } Res operator()(CLiquidityMessage &obj) const { - auto res = isPostBayfrontFork(); - return !res ? res : serialize(obj); + Require(isPostBayfrontFork()); + return serialize(obj); } Res operator()(CRemoveLiquidityMessage &obj) const { - auto res = isPostBayfrontFork(); - return !res ? res : serialize(obj); + Require(isPostBayfrontFork()); + return serialize(obj); } Res operator()(CUtxosToAccountMessage &obj) const { - auto res = isPostAMKFork(); - return !res ? res : serialize(obj); + Require(isPostAMKFork()); + return serialize(obj); } Res operator()(CAccountToUtxosMessage &obj) const { - auto res = isPostAMKFork(); - return !res ? res : serialize(obj); + Require(isPostAMKFork()); + return serialize(obj); } Res operator()(CAccountToAccountMessage &obj) const { - auto res = isPostAMKFork(); - return !res ? res : serialize(obj); + Require(isPostAMKFork()); + return serialize(obj); } Res operator()(CAnyAccountsToAccountsMessage &obj) const { - auto res = isPostBayfrontGardensFork(); - return !res ? res : serialize(obj); + Require(isPostBayfrontGardensFork()); + return serialize(obj); } Res operator()(CSmartContractMessage &obj) const { - auto res = isPostFortCanningHillFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningHillFork()); + return serialize(obj); } Res operator()(CFutureSwapMessage &obj) const { - auto res = isPostFortCanningRoadFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningRoadFork()); + return serialize(obj); } Res operator()(CCreatePoolPairMessage &obj) const { - auto res = isPostBayfrontFork(); - if (!res) { - return res; - } + Require(isPostBayfrontFork()); CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); ss >> obj.poolPair; @@ -495,17 +462,12 @@ class CCustomMetadataParseVisitor { if (static_cast(height) >= consensus.ClarkeQuayHeight && !ss.empty()) { ss >> obj.rewards; } - if (!ss.empty()) { - return Res::Err("deserialization failed: excess %d bytes", ss.size()); - } + Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); return Res::Ok(); } Res operator()(CUpdatePoolPairMessage &obj) const { - auto res = isPostBayfrontFork(); - if (!res) { - return res; - } + Require(isPostBayfrontFork()); CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); // serialize poolId as raw integer @@ -519,25 +481,19 @@ class CCustomMetadataParseVisitor { ss >> obj.rewards; } - if (!ss.empty()) { - return Res::Err("deserialization failed: excess %d bytes", ss.size()); - } + Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); return Res::Ok(); } Res operator()(CGovernanceMessage &obj) const { - auto res = isPostBayfrontFork(); - if (!res) { - return res; - } + Require(isPostBayfrontFork()); + std::string name; CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); while (!ss.empty()) { ss >> name; auto var = GovVariable::Create(name); - if (!var) { - return Res::Err("'%s': variable is not registered", name); - } + Require(var, "'%s': variable is not registered", name); ss >> *var; obj.govs.insert(std::move(var)); } @@ -545,180 +501,176 @@ class CCustomMetadataParseVisitor { } Res operator()(CGovernanceHeightMessage &obj) const { - auto res = isPostFortCanningFork(); - if (!res) { - return res; - } + Require(isPostFortCanningFork()); + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); std::string name; ss >> name; obj.govVar = GovVariable::Create(name); - if (!obj.govVar) { - return Res::Err("'%s': variable is not registered", name); - } + Require(obj.govVar, "'%s': variable is not registered", name); ss >> *obj.govVar; ss >> obj.startHeight; return Res::Ok(); } Res operator()(CAppointOracleMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CRemoveOracleAppointMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CUpdateOracleAppointMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CSetOracleDataMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXCreateOrderMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXMakeOfferMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXSubmitDFCHTLCMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXSubmitEXTHTLCMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXClaimDFCHTLCMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXCloseOrderMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CICXCloseOfferMessage &obj) const { - auto res = isPostEunosFork(); - return !res ? res : serialize(obj); + Require(isPostEunosFork()); + return serialize(obj); } Res operator()(CPoolSwapMessageV2 &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanSetCollateralTokenMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanSetLoanTokenMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanUpdateLoanTokenMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanSchemeMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CDefaultLoanSchemeMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CDestroyLoanSchemeMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CVaultMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CCloseVaultMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CUpdateVaultMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CDepositToVaultMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CWithdrawFromVaultMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CPaybackWithCollateralMessage &obj) const { - auto res = isPostFortCanningEpilogueFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningEpilogueFork()); + return serialize(obj); } Res operator()(CLoanTakeLoanMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanPaybackLoanMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CLoanPaybackLoanV2Message &obj) const { - auto res = isPostFortCanningRoadFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningRoadFork()); + return serialize(obj); } Res operator()(CAuctionBidMessage &obj) const { - auto res = isPostFortCanningFork(); - return !res ? res : serialize(obj); + Require(isPostFortCanningFork()); + return serialize(obj); } Res operator()(CBurnTokensMessage &obj) const { - auto res = isPostGrandCentralFork(); - return !res ? res : serialize(obj); + Require(isPostGrandCentralFork()); + return serialize(obj); } Res operator()(CCreatePropMessage &obj) const { - auto res = isPostGrandCentralFork(); - return !res ? res : serialize(obj); + Require(isPostGrandCentralFork()); + return serialize(obj); } Res operator()(CPropVoteMessage &obj) const { - auto res = isPostGrandCentralFork(); - return !res ? res : serialize(obj); + Require(isPostGrandCentralFork()); + return serialize(obj); } Res operator()(CGovernanceUnsetMessage &obj) const { - auto res = isPostGrandCentralFork(); - return !res ? res : serialize(obj); + Require(isPostGrandCentralFork()); + return serialize(obj); } Res operator()(CCustomTxMessageNone &) const { return Res::Ok(); } @@ -735,21 +687,18 @@ CCustomTxVisitor::CCustomTxVisitor(const CTransaction &tx, coins(coins), consensus(consensus) {} -bool CCustomTxVisitor::HasAuth(const CScript &auth) const { +Res CCustomTxVisitor::HasAuth(const CScript &auth) const { for (const auto &input : tx.vin) { const Coin &coin = coins.AccessCoin(input.prevout); - if (!coin.IsSpent() && coin.out.scriptPubKey == auth) { - return true; - } + if (!coin.IsSpent() && coin.out.scriptPubKey == auth) + return Res::Ok(); } - return false; + return Res::Err("tx must have at least one input from account owner"); } Res CCustomTxVisitor::HasCollateralAuth(const uint256 &collateralTx) const { const Coin &auth = coins.AccessCoin(COutPoint(collateralTx, 1)); // always n=1 output - if (!HasAuth(auth.out.scriptPubKey)) { - return Res::Err("tx must have at least one input from the owner"); - } + Require(HasAuth(auth.out.scriptPubKey), "tx must have at least one input from the owner"); return Res::Ok(); } @@ -776,28 +725,27 @@ Res CCustomTxVisitor::HasFoundationAuth() const { } Res CCustomTxVisitor::CheckMasternodeCreationTx() const { - if (tx.vout.size() < 2 || tx.vout[0].nValue < GetMnCreationFee(height) || tx.vout[0].nTokenId != DCT_ID{0} || - tx.vout[1].nValue != GetMnCollateralAmount(height) || tx.vout[1].nTokenId != DCT_ID{0}) { - return Res::Err("malformed tx vouts (wrong creation fee or collateral amount)"); - } + Require(tx.vout.size() >= 2 && tx.vout[0].nValue >= GetMnCreationFee(height) && tx.vout[0].nTokenId == DCT_ID{0} && + tx.vout[1].nValue == GetMnCollateralAmount(height) && tx.vout[1].nTokenId == DCT_ID{0}, + "malformed tx vouts (wrong creation fee or collateral amount)"); + return Res::Ok(); } Res CCustomTxVisitor::CheckTokenCreationTx() const { - if (tx.vout.size() < 2 || tx.vout[0].nValue < GetTokenCreationFee(height) || tx.vout[0].nTokenId != DCT_ID{0} || - tx.vout[1].nValue != GetTokenCollateralAmount() || tx.vout[1].nTokenId != DCT_ID{0}) { - return Res::Err("malformed tx vouts (wrong creation fee or collateral amount)"); - } + Require(tx.vout.size() >= 2 && tx.vout[0].nValue >= GetTokenCreationFee(height) && + tx.vout[0].nTokenId == DCT_ID{0} && tx.vout[1].nValue == GetTokenCollateralAmount() && + tx.vout[1].nTokenId == DCT_ID{0}, + "malformed tx vouts (wrong creation fee or collateral amount)"); + return Res::Ok(); } Res CCustomTxVisitor::CheckCustomTx() const { - if (static_cast(height) < consensus.EunosPayaHeight && tx.vout.size() != 2) { - return Res::Err("malformed tx vouts ((wrong number of vouts)"); - } - if (static_cast(height) >= consensus.EunosPayaHeight && tx.vout[0].nValue != 0) { - return Res::Err("malformed tx vouts, first vout must be OP_RETURN vout with value 0"); - } + if (static_cast(height) < consensus.EunosPayaHeight) + Require(tx.vout.size() == 2, "malformed tx vouts ((wrong number of vouts)"); + if (static_cast(height) >= consensus.EunosPayaHeight) + Require(tx.vout[0].nValue == 0, "malformed tx vouts, first vout must be OP_RETURN vout with value 0"); return Res::Ok(); } @@ -814,18 +762,13 @@ Res CCustomTxVisitor::TransferTokenBalance(DCT_ID id, CAmount amount, const CScr CTokenAmount tokenAmount{id, amount}; // if "from" not supplied it will only add balance on "to" address if (!from.empty()) { - auto res = mnview.SubBalance(from, tokenAmount); - if (!res) - return res; + Require(mnview.SubBalance(from, tokenAmount)); } // if "to" not supplied it will only sub balance from "form" address if (!to.empty()) { - auto res = mnview.AddBalance(to, tokenAmount); - if (!res) - return res; + Require(mnview.AddBalance(to, tokenAmount)); } - return Res::Ok(); } @@ -907,8 +850,8 @@ ResVal CCustomTxVisitor::MintableToken(DCT_ID id, if (anybodyCanMint || HasAuth(auth.out.scriptPubKey)) return result; - // Historic: in the case of DAT, it's ok to do not check foundation auth cause exact DAT owner is foundation member - // himself The above is no longer true. + // Historic: in the case of DAT, it's ok to do not check foundation auth cause exact DAT owner is foundation + // member himself The above is no longer true. if (token.IsDAT()) { // Is a DAT, check founders auth @@ -926,10 +869,7 @@ Res CCustomTxVisitor::EraseEmptyBalances(TAmounts &balances) const { for (auto it = balances.begin(), next_it = it; it != balances.end(); it = next_it) { ++next_it; - auto token = mnview.GetToken(it->first); - if (!token) { - return Res::Err("reward token %d does not exist!", it->first.v); - } + Require(mnview.GetToken(it->first), "reward token %d does not exist!", it->first.v); if (it->second == 0) { balances.erase(it); @@ -944,10 +884,7 @@ Res CCustomTxVisitor::SetShares(const CScript &owner, const TAmounts &balances) if (token && token->IsPoolShare()) { const auto bal = mnview.GetBalance(owner, balance.first); if (bal.nValue == balance.second) { - auto res = mnview.SetShare(balance.first, owner, height); - if (!res) { - return res; - } + Require(mnview.SetShare(balance.first, owner, height)); } } } @@ -960,10 +897,7 @@ Res CCustomTxVisitor::DelShares(const CScript &owner, const TAmounts &balances) if (token && token->IsPoolShare()) { const auto balance = mnview.GetBalance(owner, kv.first); if (balance.nValue == 0) { - auto res = mnview.DelShare(kv.first, owner); - if (!res) { - return res; - } + Require(mnview.DelShare(kv.first, owner)); } } } @@ -988,26 +922,20 @@ Res CCustomTxVisitor::SubBalanceDelShares(const CScript &owner, const CBalances Res CCustomTxVisitor::AddBalanceSetShares(const CScript &owner, const CBalances &balance) const { CalculateOwnerRewards(owner); - auto res = mnview.AddBalances(owner, balance); - return !res ? res : SetShares(owner, balance.balances); + Require(mnview.AddBalances(owner, balance)); + return SetShares(owner, balance.balances); } Res CCustomTxVisitor::AddBalancesSetShares(const CAccounts &accounts) const { for (const auto &account : accounts) { - auto res = AddBalanceSetShares(account.first, account.second); - if (!res) { - return res; - } + Require(AddBalanceSetShares(account.first, account.second)); } return Res::Ok(); } Res CCustomTxVisitor::SubBalancesDelShares(const CAccounts &accounts) const { for (const auto &account : accounts) { - auto res = SubBalanceDelShares(account.first, account.second); - if (!res) { - return res; - } + Require(SubBalanceDelShares(account.first, account.second)); } return Res::Ok(); } @@ -1017,9 +945,7 @@ Res CCustomTxVisitor::NormalizeTokenCurrencyPair(std::set &t for (const auto &pair : tokenCurrency) { auto token = trim_ws(pair.first).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); auto currency = trim_ws(pair.second).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); - if (token.empty() || currency.empty()) { - return Res::Err("empty token / currency"); - } + Require(!token.empty() && !currency.empty(), "empty token / currency"); trimmed.emplace(token, currency); } tokenCurrency = std::move(trimmed); @@ -1034,17 +960,15 @@ Res CCustomTxVisitor::IsOnChainGovernanceEnabled() const { CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; auto attributes = mnview.GetAttributes(); - if (!attributes) { - return Res::Err("Attributes unavailable"); - } + Require(attributes, "Attributes unavailable"); - if (!attributes->GetValue(enabledKey, false)) { - return Res::Err("Cannot create tx, on-chain governance is not enabled"); - } + Require(attributes->GetValue(enabledKey, false), "Cannot create tx, on-chain governance is not enabled"); return Res::Ok(); } +// -- -- -- -- -- -- -- -DONE + class CCustomTxApplyVisitor : public CCustomTxVisitor { uint64_t time; uint32_t txn; @@ -1063,16 +987,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { txn(txn) {} Res operator()(const CCreateMasterNodeMessage &obj) const { - auto res = CheckMasternodeCreationTx(); - if (!res) { - return res; - } + Require(CheckMasternodeCreationTx()); - if (height >= static_cast(Params().GetConsensus().EunosHeight) && !HasAuth(tx.vout[1].scriptPubKey)) { - return Res::Err("masternode creation needs owner auth"); - } + if (height >= static_cast(consensus.EunosHeight)) + Require(HasAuth(tx.vout[1].scriptPubKey), "masternode creation needs owner auth"); - if (height >= static_cast(Params().GetConsensus().EunosPayaHeight)) { + if (height >= static_cast(consensus.EunosPayaHeight)) { switch (obj.timelock) { case CMasternode::ZEROYEAR: case CMasternode::FIVEYEAR: @@ -1081,9 +1001,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { default: return Res::Err("Timelock must be set to either 0, 5 or 10 years"); } - } else if (obj.timelock != 0) { - return Res::Err("collateral timelock cannot be set below EunosPaya"); - } + } else + Require(obj.timelock == 0, "collateral timelock cannot be set below EunosPaya"); CMasternode node; CTxDestination dest; @@ -1101,9 +1020,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { node.operatorAuthAddress = obj.operatorAuthAddress; // Set masternode version2 after FC for new serialisation - if (height >= static_cast(Params().GetConsensus().FortCanningHeight)) { + if (height >= static_cast(consensus.FortCanningHeight)) node.version = CMasternode::VERSION0; - } bool duplicate{}; mnview.ForEachNewCollateral([&](const uint256 &key, CLazySerialize valueKey) { @@ -1128,27 +1046,25 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { return Res::ErrCode(CustomTxErrCodes::Fatal, "Masternode exist with that owner address pending"); } - res = mnview.CreateMasternode(tx.GetHash(), node, obj.timelock); + Require(mnview.CreateMasternode(tx.GetHash(), node, obj.timelock)); // Build coinage from the point of masternode creation - if (res) { - if (height >= static_cast(Params().GetConsensus().EunosPayaHeight)) { - for (uint8_t i{0}; i < SUBNODE_COUNT; ++i) { - mnview.SetSubNodesBlockTime(node.operatorAuthAddress, static_cast(height), i, time); - } - } else if (height >= static_cast(Params().GetConsensus().DakotaCrescentHeight)) { - mnview.SetMasternodeLastBlockTime(node.operatorAuthAddress, static_cast(height), time); - } - } - return res; + + if (height >= static_cast(consensus.EunosPayaHeight)) + for (uint8_t i{0}; i < SUBNODE_COUNT; ++i) + mnview.SetSubNodesBlockTime(node.operatorAuthAddress, static_cast(height), i, time); + + else if (height >= static_cast(consensus.DakotaCrescentHeight)) + mnview.SetMasternodeLastBlockTime(node.operatorAuthAddress, static_cast(height), time); + + return Res::Ok(); } Res operator()(const CResignMasterNodeMessage &obj) const { auto node = mnview.GetMasternode(obj); - if (!node) { - return Res::Err("node %s does not exists", obj.ToString()); - } - auto res = HasCollateralAuth(node->collateralTx.IsNull() ? static_cast(obj) : node->collateralTx); - return !res ? res : mnview.ResignMasternode(*node, obj, tx.GetHash(), height); + Require(node, "node %s does not exists", obj.ToString()); + + Require(HasCollateralAuth(node->collateralTx.IsNull() ? static_cast(obj) : node->collateralTx)); + return mnview.ResignMasternode(*node, obj, tx.GetHash(), height); } Res operator()(const CUpdateMasterNodeMessage &obj) const { @@ -1161,20 +1077,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } auto node = mnview.GetMasternode(obj.mnId); - if (!node) { - return Res::Err("masternode %s does not exists", obj.mnId.ToString()); - } + Require(node, "masternode %s does not exist", obj.mnId.ToString()); const auto collateralTx = node->collateralTx.IsNull() ? obj.mnId : node->collateralTx; - const auto res = HasCollateralAuth(collateralTx); - if (!res) { - return res; - } + Require(HasCollateralAuth(collateralTx)); auto state = node->GetState(height, mnview); - if (state != CMasternode::ENABLED) { - return Res::Err("Masternode %s is not in 'ENABLED' state", obj.mnId.ToString()); - } + Require(state == CMasternode::ENABLED, "Masternode %s is not in 'ENABLED' state", obj.mnId.ToString()); const auto attributes = mnview.GetAttributes(); assert(attributes); @@ -1347,23 +1256,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CUpdateTokenMessage &obj) const { auto pair = mnview.GetTokenByCreationTx(obj.tokenTx); - if (!pair) { - return Res::Err("token with creationTx %s does not exist", obj.tokenTx.ToString()); - } - if (pair->first == DCT_ID{0}) { - return Res::Err("Can't alter DFI token!"); // may be redundant cause DFI is 'finalized' - } + Require(pair, "token with creationTx %s does not exist", obj.tokenTx.ToString()); + Require(pair->first != DCT_ID{0}, "Can't alter DFI token!"); - if (mnview.AreTokensLocked({pair->first.v})) { - return Res::Err("Cannot update token during lock"); - } + Require(!mnview.AreTokensLocked({pair->first.v}), "Cannot update token during lock"); const auto &token = pair->second; // need to check it exectly here cause lps has no collateral auth (that checked next) - if (token.IsPoolShare()) { - return Res::Err("token %s is the LPS token! Can't alter pool share's tokens!", obj.tokenTx.ToString()); - } + Require(!token.IsPoolShare(), + "token %s is the LPS token! Can't alter pool share's tokens!", + obj.tokenTx.ToString()); // check auth, depends from token's "origins" const Coin &auth = coins.AccessCoin(COutPoint(token.creationTx, 1)); // always n=1 output @@ -1379,29 +1282,23 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { bool isFoundersToken = !databaseMembers.empty() ? databaseMembers.count(auth.out.scriptPubKey) > 0 : consensus.foundationMembers.count(auth.out.scriptPubKey) > 0; - auto res = Res::Ok(); - if (isFoundersToken && !(res = HasFoundationAuth())) { - return res; - } else if (!(res = HasCollateralAuth(token.creationTx))) { - return res; - } + if (isFoundersToken) + Require(HasFoundationAuth()); + else + Require(HasCollateralAuth(token.creationTx)); // Check for isDAT change in non-foundation token after set height - if (static_cast(height) >= consensus.BayfrontMarinaHeight) { + if (static_cast(height) >= consensus.BayfrontMarinaHeight) // check foundation auth - if (obj.token.IsDAT() != token.IsDAT() && - !HasFoundationAuth()) { // no need to check Authority if we don't create isDAT - return Res::Err("can't set isDAT to true, tx not from foundation member"); - } - } + Require(obj.token.IsDAT() == token.IsDAT() || HasFoundationAuth(), + "can't set isDAT to true, tx not from foundation member"); CTokenImplementation updatedToken{obj.token}; updatedToken.creationTx = token.creationTx; updatedToken.destructionTx = token.destructionTx; updatedToken.destructionHeight = token.destructionHeight; - if (height >= static_cast(consensus.FortCanningHeight)) { + if (static_cast(height) >= consensus.FortCanningHeight) updatedToken.symbol = trim_ws(updatedToken.symbol).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); - } return mnview.UpdateToken(updatedToken); } @@ -1560,7 +1457,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (dailyLimit != -1 * COIN && totalDaily > dailyLimit) return Res::Err( - "You will exceed global daily maximum consortium mint limit for %s token by minting this amount.", + "You will exceed global daily maximum consortium mint limit for %s token by minting this " + "amount.", token->symbol); attributes->SetValue(consortiumMintedKey, globalBalances); @@ -1599,8 +1497,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { ownerAddress = obj.from; auto attributes = mnview.GetAttributes(); - if (!attributes) - return Res::Err("Cannot read from attributes gov variable!"); + Require(attributes, "Cannot read from attributes gov variable!"); CDataStructureV0 membersKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MemberValues}; const auto members = attributes->GetValue(membersKey, CConsortiumMembers{}); @@ -1651,16 +1548,11 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CCreatePoolPairMessage &obj) const { // check foundation auth - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member"); - } - if (obj.poolPair.commission < 0 || obj.poolPair.commission > COIN) { - return Res::Err("wrong commission"); - } + Require(HasFoundationAuth()); + Require(obj.poolPair.commission >= 0 && obj.poolPair.commission <= COIN, "wrong commission"); - if (height >= static_cast(Params().GetConsensus().FortCanningCrunchHeight) && - obj.pairSymbol.find('/') != std::string::npos) { - return Res::Err("token symbol should not contain '/'"); + if (height >= static_cast(Params().GetConsensus().FortCanningCrunchHeight)) { + Require(obj.pairSymbol.find('/') == std::string::npos, "token symbol should not contain '/'"); } /// @todo ownerAddress validity checked only in rpc. is it enough? @@ -1671,14 +1563,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { auto &rewards = poolPair.rewards; auto tokenA = mnview.GetToken(poolPair.idTokenA); - if (!tokenA) { - return Res::Err("token %s does not exist!", poolPair.idTokenA.ToString()); - } + Require(tokenA, "token %s does not exist!", poolPair.idTokenA.ToString()); auto tokenB = mnview.GetToken(poolPair.idTokenB); - if (!tokenB) { - return Res::Err("token %s does not exist!", poolPair.idTokenB.ToString()); - } + Require(tokenB, "token %s does not exist!", poolPair.idTokenB.ToString()); const auto symbolLength = height >= static_cast(consensus.FortCanningHeight) ? CToken::MAX_TOKEN_POOLPAIR_LENGTH @@ -1699,17 +1587,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { token.creationHeight = height; auto tokenId = mnview.CreateToken(token); - if (!tokenId) { - return std::move(tokenId); - } + Require(tokenId); rewards = obj.rewards; if (!rewards.balances.empty()) { // Check tokens exist and remove empty reward amounts - auto res = EraseEmptyBalances(rewards.balances); - if (!res) { - return res; - } + Require(EraseEmptyBalances(rewards.balances)); } return mnview.SetPoolPair(tokenId, height, poolPair); @@ -1717,9 +1600,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CUpdatePoolPairMessage &obj) const { // check foundation auth - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member"); - } + Require(HasFoundationAuth()); auto rewards = obj.rewards; if (!rewards.balances.empty()) { @@ -1728,10 +1609,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { rewards.balances.cbegin()->first == DCT_ID{std::numeric_limits::max()} && rewards.balances.cbegin()->second == std::numeric_limits::max())) { // Check if tokens exist and remove empty reward amounts - auto res = EraseEmptyBalances(rewards.balances); - if (!res) { - return res; - } + Require(EraseEmptyBalances(rewards.balances)); } } return mnview.UpdatePoolPair(obj.poolId, height, obj.status, obj.commission, obj.ownerAddress, rewards); @@ -1739,74 +1617,57 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CPoolSwapMessage &obj) const { // check auth - if (!HasAuth(obj.from)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(obj.from)); return CPoolSwap(obj, height).ExecuteSwap(mnview, {}); } Res operator()(const CPoolSwapMessageV2 &obj) const { // check auth - if (!HasAuth(obj.swapInfo.from)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(obj.swapInfo.from)); return CPoolSwap(obj.swapInfo, height).ExecuteSwap(mnview, obj.poolIDs); } Res operator()(const CLiquidityMessage &obj) const { CBalances sumTx = SumAllTransfers(obj.from); - if (sumTx.balances.size() != 2) { - return Res::Err("the pool pair requires two tokens"); - } + Require(sumTx.balances.size() == 2, "the pool pair requires two tokens"); std::pair amountA = *sumTx.balances.begin(); std::pair amountB = *(std::next(sumTx.balances.begin(), 1)); // checked internally too. remove here? - if (amountA.second <= 0 || amountB.second <= 0) { - return Res::Err("amount cannot be less than or equal to zero"); - } + Require(amountA.second > 0 && amountB.second > 0, "amount cannot be less than or equal to zero"); auto pair = mnview.GetPoolPair(amountA.first, amountB.first); - if (!pair) { - return Res::Err("there is no such pool pair"); - } + Require(pair, "there is no such pool pair"); for (const auto &kv : obj.from) { - if (!HasAuth(kv.first)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(kv.first)); } for (const auto &kv : obj.from) { CalculateOwnerRewards(kv.first); - auto res = mnview.SubBalances(kv.first, kv.second); - if (!res) { - return res; - } + Require(mnview.SubBalances(kv.first, kv.second)); } const auto &lpTokenID = pair->first; auto &pool = pair->second; // normalize A & B to correspond poolpair's tokens - if (amountA.first != pool.idTokenA) { + if (amountA.first != pool.idTokenA) std::swap(amountA, amountB); - } bool slippageProtection = static_cast(height) >= consensus.BayfrontMarinaHeight; - auto res = pool.AddLiquidity( + Require(pool.AddLiquidity( amountA.second, amountB.second, [&] /*onMint*/ (CAmount liqAmount) { CBalances balance{TAmounts{{lpTokenID, liqAmount}}}; return AddBalanceSetShares(obj.shareAddress, balance); }, - slippageProtection); - - return !res ? res : mnview.SetPoolPair(lpTokenID, height, pool); + slippageProtection)); + return mnview.SetPoolPair(lpTokenID, height, pool); } Res operator()(const CRemoveLiquidityMessage &obj) const { @@ -1814,53 +1675,40 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { auto amount = obj.amount; // checked internally too. remove here? - if (amount.nValue <= 0) { - return Res::Err("amount cannot be less than or equal to zero"); - } + Require(amount.nValue > 0, "amount cannot be less than or equal to zero"); auto pair = mnview.GetPoolPair(amount.nTokenId); - if (!pair) { - return Res::Err("there is no such pool pair"); - } + Require(pair, "there is no such pool pair"); - if (!HasAuth(from)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(from)); CPoolPair &pool = pair.value(); // subtract liq.balance BEFORE RemoveLiquidity call to check balance correctness - { - CBalances balance{TAmounts{{amount.nTokenId, amount.nValue}}}; - auto res = SubBalanceDelShares(from, balance); - if (!res) { - return res; - } - } + CBalances balance{TAmounts{{amount.nTokenId, amount.nValue}}}; + Require(SubBalanceDelShares(from, balance)); - auto res = pool.RemoveLiquidity(amount.nValue, [&](CAmount amountA, CAmount amountB) { + Require(pool.RemoveLiquidity(amount.nValue, [&](CAmount amountA, CAmount amountB) { CalculateOwnerRewards(from); CBalances balances{ TAmounts{{pool.idTokenA, amountA}, {pool.idTokenB, amountB}} }; return mnview.AddBalances(from, balances); - }); + })); - return !res ? res : mnview.SetPoolPair(amount.nTokenId, height, pool); + return mnview.SetPoolPair(amount.nTokenId, height, pool); } Res operator()(const CUtxosToAccountMessage &obj) const { // check enough tokens are "burnt" - const auto burnt = BurntTokens(tx); - if (!burnt) { - return burnt; - } + auto burnt = BurntTokens(tx); + Require(burnt); const auto mustBeBurnt = SumAllTransfers(obj.to); - if (*burnt.val != mustBeBurnt) { - return Res::Err( - "transfer tokens mismatch burnt tokens: (%s) != (%s)", mustBeBurnt.ToString(), burnt.val->ToString()); - } + Require(*burnt.val == mustBeBurnt, + "transfer tokens mismatch burnt tokens: (%s) != (%s)", + mustBeBurnt.ToString(), + burnt.val->ToString()); // transfer return AddBalancesSetShares(obj.to); @@ -1868,28 +1716,21 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAccountToUtxosMessage &obj) const { // check auth - if (!HasAuth(obj.from)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(obj.from)); // check that all tokens are minted, and no excess tokens are minted auto minted = MintedTokens(tx, obj.mintingOutputsStart); - if (!minted) { - return std::move(minted); - } + Require(minted); - if (obj.balances != *minted.val) { - return Res::Err("amount of minted tokens in UTXOs and metadata do not match: (%s) != (%s)", - minted.val->ToString(), - obj.balances.ToString()); - } + Require(obj.balances == *minted.val, + "amount of minted tokens in UTXOs and metadata do not match: (%s) != (%s)", + minted.val->ToString(), + obj.balances.ToString()); // block for non-DFI transactions for (const auto &kv : obj.balances.balances) { const DCT_ID &tokenId = kv.first; - if (tokenId != DCT_ID{0}) { - return Res::Err("only available for DFI transactions"); - } + Require(tokenId == DCT_ID{0}, "only available for DFI transactions"); } // transfer @@ -1898,69 +1739,55 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAccountToAccountMessage &obj) const { // check auth - if (!HasAuth(obj.from)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(obj.from)); // transfer - auto res = SubBalanceDelShares(obj.from, SumAllTransfers(obj.to)); - return !res ? res : AddBalancesSetShares(obj.to); + Require(SubBalanceDelShares(obj.from, SumAllTransfers(obj.to))); + return AddBalancesSetShares(obj.to); } Res HandleDFIP2201Contract(const CSmartContractMessage &obj) const { const auto attributes = mnview.GetAttributes(); - if (!attributes) - return Res::Err("Attributes unavailable"); + Require(attributes, "Attributes unavailable"); CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::Active}; - if (!attributes->GetValue(activeKey, false)) - return Res::Err("DFIP2201 smart contract is not enabled"); + Require(attributes->GetValue(activeKey, false), "DFIP2201 smart contract is not enabled"); - if (obj.name != SMART_CONTRACT_DFIP_2201) - return Res::Err("DFIP2201 contract mismatch - got: " + obj.name); + Require(obj.name == SMART_CONTRACT_DFIP_2201, "DFIP2201 contract mismatch - got: " + obj.name); - if (obj.accounts.size() != 1) - return Res::Err("Only one address entry expected for " + obj.name); + Require(obj.accounts.size() == 1, "Only one address entry expected for " + obj.name); - if (obj.accounts.begin()->second.balances.size() != 1) - return Res::Err("Only one amount entry expected for " + obj.name); + Require(obj.accounts.begin()->second.balances.size() == 1, "Only one amount entry expected for " + obj.name); const auto &script = obj.accounts.begin()->first; - if (!HasAuth(script)) - return Res::Err("Must have at least one input from supplied address"); + Require(HasAuth(script), "Must have at least one input from supplied address"); const auto &id = obj.accounts.begin()->second.balances.begin()->first; const auto &amount = obj.accounts.begin()->second.balances.begin()->second; - if (amount <= 0) - return Res::Err("Amount out of range"); + Require(amount > 0, "Amount out of range"); CDataStructureV0 minSwapKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::MinSwap}; auto minSwap = attributes->GetValue(minSwapKey, CAmount{0}); - if (minSwap && amount < minSwap) { - return Res::Err("Below minimum swapable amount, must be at least " + GetDecimaleString(minSwap) + " BTC"); - } + Require(amount >= minSwap, + "Below minimum swapable amount, must be at least " + GetDecimaleString(minSwap) + " BTC"); const auto token = mnview.GetToken(id); - if (!token) - return Res::Err("Specified token not found"); + Require(token, "Specified token not found"); - if (token->symbol != "BTC" || token->name != "Bitcoin" || !token->IsDAT()) - return Res::Err("Only Bitcoin can be swapped in " + obj.name); + Require(token->symbol == "BTC" && token->name == "Bitcoin" && token->IsDAT(), + "Only Bitcoin can be swapped in " + obj.name); - auto res = mnview.SubBalance(script, {id, amount}); - if (!res) - return res; + Require(mnview.SubBalance(script, {id, amount})); const CTokenCurrencyPair btcUsd{"BTC", "USD"}; const CTokenCurrencyPair dfiUsd{"DFI", "USD"}; bool useNextPrice{false}, requireLivePrice{true}; auto resVal = mnview.GetValidatedIntervalPrice(btcUsd, useNextPrice, requireLivePrice); - if (!resVal) - return std::move(resVal); + Require(resVal); CDataStructureV0 premiumKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::Premium}; auto premium = attributes->GetValue(premiumKey, CAmount{2500000}); @@ -1968,31 +1795,23 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const auto &btcPrice = MultiplyAmounts(*resVal.val, premium + COIN); resVal = mnview.GetValidatedIntervalPrice(dfiUsd, useNextPrice, requireLivePrice); - if (!resVal) - return std::move(resVal); + Require(resVal); const auto totalDFI = MultiplyAmounts(DivideAmounts(btcPrice, *resVal.val), amount); - res = mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI}); - if (!res) - return res; + Require(mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI})); - res = mnview.AddBalance(script, {{0}, totalDFI}); - if (!res) - return res; + Require(mnview.AddBalance(script, {{0}, totalDFI})); return Res::Ok(); } Res operator()(const CSmartContractMessage &obj) const { - if (obj.accounts.empty()) { - return Res::Err("Contract account parameters missing"); - } + Require(!obj.accounts.empty(), "Contract account parameters missing"); auto contracts = Params().GetConsensus().smartContracts; auto contract = contracts.find(obj.name); - if (contract == contracts.end()) - return Res::Err("Specified smart contract not found"); + Require(contract != contracts.end(), "Specified smart contract not found"); // Convert to switch when it's long enough. if (obj.name == SMART_CONTRACT_DFIP_2201) @@ -2002,14 +1821,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CFutureSwapMessage &obj) const { - if (!HasAuth(obj.owner)) { - return Res::Err("Transaction must have at least one input from owner"); - } + Require(HasAuth(obj.owner), "Transaction must have at least one input from owner"); const auto attributes = mnview.GetAttributes(); - if (!attributes) { - return Res::Err("Attributes unavailable"); - } + Require(attributes, "Attributes unavailable"); bool dfiToDUSD = !obj.source.nTokenId.v; const auto paramID = dfiToDUSD ? ParamIDs::DFIP2206F : ParamIDs::DFIP2203; @@ -2017,78 +1832,57 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 activeKey{AttributeTypes::Param, paramID, DFIPKeys::Active}; CDataStructureV0 blockKey{AttributeTypes::Param, paramID, DFIPKeys::BlockPeriod}; CDataStructureV0 rewardKey{AttributeTypes::Param, paramID, DFIPKeys::RewardPct}; - if (!attributes->GetValue(activeKey, false) || !attributes->CheckKey(blockKey) || - !attributes->CheckKey(rewardKey)) { - return Res::Err("%s not currently active", dfiToDUSD ? "DFIP2206F" : "DFIP2203"); - } + + Require( + attributes->GetValue(activeKey, false) && attributes->CheckKey(blockKey) && attributes->CheckKey(rewardKey), + "%s not currently active", + dfiToDUSD ? "DFIP2206F" : "DFIP2203"); CDataStructureV0 startKey{AttributeTypes::Param, paramID, DFIPKeys::StartBlock}; - if (const auto startBlock = attributes->GetValue(startKey, CAmount{}); height < startBlock) { - return Res::Err("%s not active until block %d", dfiToDUSD ? "DFIP2206F" : "DFIP2203", startBlock); + if (const auto startBlock = attributes->GetValue(startKey, CAmount{})) { + Require( + height >= startBlock, "%s not active until block %d", dfiToDUSD ? "DFIP2206F" : "DFIP2203", startBlock); } - if (obj.source.nValue <= 0) { - return Res::Err("Source amount must be more than zero"); - } + Require(obj.source.nValue > 0, "Source amount must be more than zero"); const auto source = mnview.GetLoanTokenByID(obj.source.nTokenId); - if (!dfiToDUSD && !source) { - return Res::Err("Could not get source loan token %d", obj.source.nTokenId.v); - } + Require(dfiToDUSD || source, "Could not get source loan token %d", obj.source.nTokenId.v); if (!dfiToDUSD && source->symbol == "DUSD") { CDataStructureV0 tokenKey{AttributeTypes::Token, obj.destination, TokenKeys::DFIP2203Enabled}; const auto enabled = attributes->GetValue(tokenKey, true); - if (!enabled) { - return Res::Err("DFIP2203 currently disabled for token %d", obj.destination); - } + Require(enabled, "DFIP2203 currently disabled for token %d", obj.destination); const auto loanToken = mnview.GetLoanTokenByID({obj.destination}); - if (!loanToken) { - return Res::Err("Could not get destination loan token %d. Set valid destination.", obj.destination); - } + Require(loanToken, "Could not get destination loan token %d. Set valid destination.", obj.destination); - if (mnview.AreTokensLocked({obj.destination})) { - return Res::Err("Cannot create future swap for locked token"); - } + Require(!mnview.AreTokensLocked({obj.destination}), "Cannot create future swap for locked token"); } else { if (!dfiToDUSD) { - if (obj.destination != 0) { - return Res::Err("Destination should not be set when source amount is dToken or DFI"); - } + Require(obj.destination == 0, "Destination should not be set when source amount is dToken or DFI"); - if (mnview.AreTokensLocked({obj.source.nTokenId.v})) { - return Res::Err("Cannot create future swap for locked token"); - } + Require(!mnview.AreTokensLocked({obj.source.nTokenId.v}), "Cannot create future swap for locked token"); CDataStructureV0 tokenKey{AttributeTypes::Token, obj.source.nTokenId.v, TokenKeys::DFIP2203Enabled}; const auto enabled = attributes->GetValue(tokenKey, true); - if (!enabled) { - return Res::Err("DFIP2203 currently disabled for token %s", obj.source.nTokenId.ToString()); - } + Require(enabled, "DFIP2203 currently disabled for token %s", obj.source.nTokenId.ToString()); } else { DCT_ID id{}; const auto token = mnview.GetTokenGuessId("DUSD", id); - if (!token) { - return Res::Err("No DUSD token defined"); - } + Require(token, "No DUSD token defined"); - if (!mnview.GetFixedIntervalPrice({"DFI", "USD"})) { - return Res::Err("DFI / DUSD fixed interval price not found"); - } + Require(mnview.GetFixedIntervalPrice({"DFI", "USD"}), "DFI / DUSD fixed interval price not found"); - if (obj.destination != id.v) { - return Res::Err("Incorrect destination defined for DFI swap, DUSD destination expected id: %d", - id.v); - } + Require(obj.destination == id.v, + "Incorrect destination defined for DFI swap, DUSD destination expected id: %d", + id.v); } } const auto contractType = dfiToDUSD ? SMART_CONTRACT_DFIP2206F : SMART_CONTRACT_DFIP_2203; const auto contractAddressValue = GetFutureSwapContractAddress(contractType); - if (!contractAddressValue) { - return contractAddressValue; - } + Require(contractAddressValue); const auto economyKey = dfiToDUSD ? EconomyKeys::DFIP2206FCurrent : EconomyKeys::DFIP2203Current; CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, economyKey}; @@ -2137,47 +1931,28 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } } - auto res = totalFutures.Sub(obj.source.nValue); - if (!res) { - return res; - } + Require(totalFutures.Sub(obj.source.nValue)); if (totalFutures.nValue > 0) { Res res{}; if (!dfiToDUSD) { - res = mnview.StoreFuturesUserValues({height, obj.owner, txn}, {totalFutures, obj.destination}); + Require(mnview.StoreFuturesUserValues({height, obj.owner, txn}, {totalFutures, obj.destination})); } else { - res = mnview.StoreFuturesDUSD({height, obj.owner, txn}, totalFutures.nValue); - } - if (!res) { - return res; + Require(mnview.StoreFuturesDUSD({height, obj.owner, txn}, totalFutures.nValue)); } } - res = TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, *contractAddressValue, obj.owner); - if (!res) { - return res; - } + Require(TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, *contractAddressValue, obj.owner)); - res = balances.Sub(obj.source); - if (!res) { - return res; - } + Require(balances.Sub(obj.source)); } else { - auto res = TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, obj.owner, *contractAddressValue); - if (!res) { - return res; - } + Require(TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, obj.owner, *contractAddressValue)); if (!dfiToDUSD) { - res = mnview.StoreFuturesUserValues({height, obj.owner, txn}, {obj.source, obj.destination}); + Require(mnview.StoreFuturesUserValues({height, obj.owner, txn}, {obj.source, obj.destination})); } else { - res = mnview.StoreFuturesDUSD({height, obj.owner, txn}, obj.source.nValue); + Require(mnview.StoreFuturesDUSD({height, obj.owner, txn}, obj.source.nValue)); } - if (!res) { - return res; - } - balances.Add(obj.source); } @@ -2191,31 +1966,25 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAnyAccountsToAccountsMessage &obj) const { // check auth for (const auto &kv : obj.from) { - if (!HasAuth(kv.first)) { - return Res::Err("tx must have at least one input from account owner"); - } + Require(HasAuth(kv.first)); } // compare const auto sumFrom = SumAllTransfers(obj.from); const auto sumTo = SumAllTransfers(obj.to); - if (sumFrom != sumTo) { - return Res::Err("sum of inputs (from) != sum of outputs (to)"); - } + Require(sumFrom == sumTo, "sum of inputs (from) != sum of outputs (to)"); // transfer // substraction - auto res = SubBalancesDelShares(obj.from); + Require(SubBalancesDelShares(obj.from)); // addition - return !res ? res : AddBalancesSetShares(obj.to); + return AddBalancesSetShares(obj.to); } Res operator()(const CGovernanceMessage &obj) const { // check foundation auth - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member"); - } + Require(HasFoundationAuth()); for (const auto &gov : obj.govs) { Res res{}; @@ -2426,14 +2195,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } COracle oracle; static_cast(oracle) = obj.newOracleAppoint; - auto res = NormalizeTokenCurrencyPair(oracle.availablePairs); - return !res ? res : mnview.UpdateOracle(obj.oracleId, std::move(oracle)); + Require(NormalizeTokenCurrencyPair(oracle.availablePairs)); + return mnview.UpdateOracle(obj.oracleId, std::move(oracle)); } Res operator()(const CRemoveOracleAppointMessage &obj) const { - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member"); - } + Require(HasFoundationAuth()); + return mnview.RemoveOracle(obj.oracleId); } @@ -2464,9 +2232,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CICXCreateOrderMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXOrderImplemetation order; static_cast(order) = obj; @@ -2474,29 +2240,24 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { order.creationTx = tx.GetHash(); order.creationHeight = height; - if (!HasAuth(order.ownerAddress)) - return Res::Err("tx must have at least one input from order owner"); + Require(HasAuth(order.ownerAddress), "tx must have at least one input from order owner"); - if (!mnview.GetToken(order.idToken)) - return Res::Err("token %s does not exist!", order.idToken.ToString()); + Require(mnview.GetToken(order.idToken), "token %s does not exist!", order.idToken.ToString()); if (order.orderType == CICXOrder::TYPE_INTERNAL) { - if (!order.receivePubkey.IsFullyValid()) - return Res::Err("receivePubkey must be valid pubkey"); + Require(order.receivePubkey.IsFullyValid(), "receivePubkey must be valid pubkey"); // subtract the balance from tokenFrom to dedicate them for the order CScript txidAddr(order.creationTx.begin(), order.creationTx.end()); CalculateOwnerRewards(order.ownerAddress); - res = TransferTokenBalance(order.idToken, order.amountFrom, order.ownerAddress, txidAddr); + Require(TransferTokenBalance(order.idToken, order.amountFrom, order.ownerAddress, txidAddr)); } - return !res ? res : mnview.ICXCreateOrder(order); + return mnview.ICXCreateOrder(order); } Res operator()(const CICXMakeOfferMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXMakeOfferImplemetation makeoffer; static_cast(makeoffer) = obj; @@ -2504,18 +2265,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { makeoffer.creationTx = tx.GetHash(); makeoffer.creationHeight = height; - if (!HasAuth(makeoffer.ownerAddress)) - return Res::Err("tx must have at least one input from order owner"); + Require(HasAuth(makeoffer.ownerAddress), "tx must have at least one input from order owner"); auto order = mnview.GetICXOrderByCreationTx(makeoffer.orderTx); - if (!order) - return Res::Err("order with creation tx " + makeoffer.orderTx.GetHex() + " does not exists!"); + Require(order, "order with creation tx " + makeoffer.orderTx.GetHex() + " does not exists!"); auto expiry = static_cast(height) < consensus.EunosPayaHeight ? CICXMakeOffer::DEFAULT_EXPIRY : CICXMakeOffer::EUNOSPAYA_DEFAULT_EXPIRY; - if (makeoffer.expiry < expiry) - return Res::Err("offer expiry must be greater than %d!", expiry - 1); + Require(makeoffer.expiry >= expiry, "offer expiry must be greater than %d!", expiry - 1); CScript txidAddr(makeoffer.creationTx.begin(), makeoffer.creationTx.end()); @@ -2523,8 +2281,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // calculating takerFee makeoffer.takerFee = CalculateTakerFee(makeoffer.amount); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - if (!makeoffer.receivePubkey.IsFullyValid()) - return Res::Err("receivePubkey must be valid pubkey"); + Require(makeoffer.receivePubkey.IsFullyValid(), "receivePubkey must be valid pubkey"); // calculating takerFee CAmount BTCAmount(static_cast( @@ -2534,16 +2291,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // locking takerFee in offer txidaddr CalculateOwnerRewards(makeoffer.ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, makeoffer.takerFee, makeoffer.ownerAddress, txidAddr); + Require(TransferTokenBalance(DCT_ID{0}, makeoffer.takerFee, makeoffer.ownerAddress, txidAddr)); - return !res ? res : mnview.ICXMakeOffer(makeoffer); + return mnview.ICXMakeOffer(makeoffer); } Res operator()(const CICXSubmitDFCHTLCMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) { - return res; - } + Require(CheckCustomTx()); CICXSubmitDFCHTLCImplemetation submitdfchtlc; static_cast(submitdfchtlc) = obj; @@ -2552,27 +2306,22 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { submitdfchtlc.creationHeight = height; auto offer = mnview.GetICXMakeOfferByCreationTx(submitdfchtlc.offerTx); - if (!offer) - return Res::Err("offer with creation tx %s does not exists!", submitdfchtlc.offerTx.GetHex()); + Require(offer, "offer with creation tx %s does not exists!", submitdfchtlc.offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) - return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); - if (order->creationHeight + order->expiry < height + submitdfchtlc.timeout) - return Res::Err("order will expire before dfc htlc expires!"); - - if (mnview.HasICXSubmitDFCHTLCOpen(submitdfchtlc.offerTx)) - return Res::Err("dfc htlc already submitted!"); + Require(order->creationHeight + order->expiry >= height + submitdfchtlc.timeout, + "order will expire before dfc htlc expires!"); + Require(!mnview.HasICXSubmitDFCHTLCOpen(submitdfchtlc.offerTx), "dfc htlc already submitted!"); CScript srcAddr; if (order->orderType == CICXOrder::TYPE_INTERNAL) { // check auth - if (!HasAuth(order->ownerAddress)) - return Res::Err("tx must have at least one input from order owner"); - - if (!mnview.HasICXMakeOfferOpen(offer->orderTx, submitdfchtlc.offerTx)) - return Res::Err("offerTx (%s) has expired", submitdfchtlc.offerTx.GetHex()); + Require(HasAuth(order->ownerAddress), "tx must have at least one input from order owner"); + Require(mnview.HasICXMakeOfferOpen(offer->orderTx, submitdfchtlc.offerTx), + "offerTx (%s) has expired", + submitdfchtlc.offerTx.GetHex()); uint32_t timeout; if (static_cast(height) < consensus.EunosPayaHeight) @@ -2580,43 +2329,32 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { else timeout = CICXSubmitDFCHTLC::EUNOSPAYA_MINIMUM_TIMEOUT; - if (submitdfchtlc.timeout < timeout) - return Res::Err("timeout must be greater than %d", timeout - 1); + Require(submitdfchtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); srcAddr = CScript(order->creationTx.begin(), order->creationTx.end()); CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - CAmount calcAmount(static_cast( - (arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)) - .GetLow64())); - if (calcAmount > offer->amount) - return Res::Err("amount must be lower or equal the offer one"); + auto calcAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); + Require(calcAmount <= offer->amount, "amount must be lower or equal the offer one"); CAmount takerFee = offer->takerFee; // EunosPaya: calculating adjusted takerFee only if amount in htlc different than in offer if (static_cast(height) >= consensus.EunosPayaHeight) { if (calcAmount < offer->amount) { - CAmount BTCAmount(static_cast( - (arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)) - .GetLow64())); - takerFee = static_cast( - (arith_uint256(BTCAmount) * arith_uint256(offer->takerFee) / arith_uint256(offer->amount)) - .GetLow64()); + auto BTCAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); + takerFee = (arith_uint256(BTCAmount) * offer->takerFee / offer->amount).GetLow64(); } } else { - CAmount BTCAmount(static_cast( - (arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)) - .GetLow64())); - takerFee = CalculateTakerFee(BTCAmount); + auto BTCAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); + takerFee = CalculateTakerFee(BTCAmount); } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { CalculateOwnerRewards(offer->ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress); - if (!res) - return res; + Require( + TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress)); // update the offer with adjusted takerFee offer->takerFee = takerFee; @@ -2624,38 +2362,31 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // burn takerFee - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress)); // burn makerDeposit CalculateOwnerRewards(order->ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress)); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { // check auth - if (!HasAuth(offer->ownerAddress)) - return Res::Err("tx must have at least one input from offer owner"); + Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); srcAddr = offer->ownerAddress; CalculateOwnerRewards(offer->ownerAddress); auto exthtlc = mnview.HasICXSubmitEXTHTLCOpen(submitdfchtlc.offerTx); - if (!exthtlc) - return Res::Err("offer (%s) needs to have ext htlc submitted first, but no external htlc found!", - submitdfchtlc.offerTx.GetHex()); + Require(exthtlc, + "offer (%s) needs to have ext htlc submitted first, but no external htlc found!", + submitdfchtlc.offerTx.GetHex()); - CAmount calcAmount(static_cast( - (arith_uint256(exthtlc->amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); - if (submitdfchtlc.amount != calcAmount) - return Res::Err("amount must be equal to calculated exthtlc amount"); + auto calcAmount = MultiplyAmounts(exthtlc->amount, order->orderPrice); + Require(submitdfchtlc.amount == calcAmount, "amount must be equal to calculated exthtlc amount"); - if (submitdfchtlc.hash != exthtlc->hash) - return Res::Err("Invalid hash, dfc htlc hash is different than extarnal htlc hash - %s != %s", - submitdfchtlc.hash.GetHex(), - exthtlc->hash.GetHex()); + Require(submitdfchtlc.hash == exthtlc->hash, + "Invalid hash, dfc htlc hash is different than extarnal htlc hash - %s != %s", + submitdfchtlc.hash.GetHex(), + exthtlc->hash.GetHex()); uint32_t timeout, btcBlocksInDfi; if (static_cast(height) < consensus.EunosPayaHeight) { @@ -2666,24 +2397,20 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { btcBlocksInDfi = CICXSubmitEXTHTLC::BTC_BLOCKS_IN_DFI_BLOCKS; } - if (submitdfchtlc.timeout < timeout) - return Res::Err("timeout must be greater than %d", timeout - 1); - - if (submitdfchtlc.timeout >= (exthtlc->creationHeight + (exthtlc->timeout * btcBlocksInDfi)) - height) - return Res::Err("timeout must be less than expiration period of 1st htlc in DFI blocks"); + Require(submitdfchtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); + Require(submitdfchtlc.timeout < (exthtlc->creationHeight + (exthtlc->timeout * btcBlocksInDfi)) - height, + "timeout must be less than expiration period of 1st htlc in DFI blocks"); } // subtract the balance from order txidaddr or offer owner address and dedicate them for the dfc htlc CScript htlcTxidAddr(submitdfchtlc.creationTx.begin(), submitdfchtlc.creationTx.end()); - res = TransferTokenBalance(order->idToken, submitdfchtlc.amount, srcAddr, htlcTxidAddr); - return !res ? res : mnview.ICXSubmitDFCHTLC(submitdfchtlc); + Require(TransferTokenBalance(order->idToken, submitdfchtlc.amount, srcAddr, htlcTxidAddr)); + return mnview.ICXSubmitDFCHTLC(submitdfchtlc); } Res operator()(const CICXSubmitEXTHTLCMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXSubmitEXTHTLCImplemetation submitexthtlc; static_cast(submitexthtlc) = obj; @@ -2692,36 +2419,29 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { submitexthtlc.creationHeight = height; auto offer = mnview.GetICXMakeOfferByCreationTx(submitexthtlc.offerTx); - if (!offer) - return Res::Err("order with creation tx %s does not exists!", submitexthtlc.offerTx.GetHex()); + Require(offer, "order with creation tx %s does not exists!", submitexthtlc.offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) - return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); - if (order->creationHeight + order->expiry < - height + (submitexthtlc.timeout * CICXSubmitEXTHTLC::BTC_BLOCKS_IN_DFI_BLOCKS)) - return Res::Err("order will expire before ext htlc expires!"); + Require(order->creationHeight + order->expiry >= + height + (submitexthtlc.timeout * CICXSubmitEXTHTLC::BTC_BLOCKS_IN_DFI_BLOCKS), + "order will expire before ext htlc expires!"); - if (mnview.HasICXSubmitEXTHTLCOpen(submitexthtlc.offerTx)) - return Res::Err("ext htlc already submitted!"); + Require(!mnview.HasICXSubmitEXTHTLCOpen(submitexthtlc.offerTx), "ext htlc already submitted!"); if (order->orderType == CICXOrder::TYPE_INTERNAL) { - if (!HasAuth(offer->ownerAddress)) - return Res::Err("tx must have at least one input from offer owner"); + Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); auto dfchtlc = mnview.HasICXSubmitDFCHTLCOpen(submitexthtlc.offerTx); - if (!dfchtlc) - return Res::Err("offer (%s) needs to have dfc htlc submitted first, but no dfc htlc found!", - submitexthtlc.offerTx.GetHex()); - - CAmount calcAmount(static_cast( - (arith_uint256(dfchtlc->amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)).GetLow64())); - if (submitexthtlc.amount != calcAmount) - return Res::Err("amount must be equal to calculated dfchtlc amount"); + Require(dfchtlc, + "offer (%s) needs to have dfc htlc submitted first, but no dfc htlc found!", + submitexthtlc.offerTx.GetHex()); - if (submitexthtlc.hash != dfchtlc->hash) - return Res::Err("Invalid hash, external htlc hash is different than dfc htlc hash"); + auto calcAmount = MultiplyAmounts(dfchtlc->amount, order->orderPrice); + Require(submitexthtlc.amount == calcAmount, "amount must be equal to calculated dfchtlc amount"); + Require(submitexthtlc.hash == dfchtlc->hash, + "Invalid hash, external htlc hash is different than dfc htlc hash"); uint32_t timeout, btcBlocksInDfi; if (static_cast(height) < consensus.EunosPayaHeight) { @@ -2732,17 +2452,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { btcBlocksInDfi = CICXSubmitEXTHTLC::EUNOSPAYA_BTC_BLOCKS_IN_DFI_BLOCKS; } - if (submitexthtlc.timeout < timeout) - return Res::Err("timeout must be greater than %d", timeout - 1); + Require(submitexthtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); + Require(submitexthtlc.timeout * btcBlocksInDfi < (dfchtlc->creationHeight + dfchtlc->timeout) - height, + "timeout must be less than expiration period of 1st htlc in DFC blocks"); - if (submitexthtlc.timeout * btcBlocksInDfi >= (dfchtlc->creationHeight + dfchtlc->timeout) - height) - return Res::Err("timeout must be less than expiration period of 1st htlc in DFC blocks"); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - if (!HasAuth(order->ownerAddress)) - return Res::Err("tx must have at least one input from order owner"); - - if (!mnview.HasICXMakeOfferOpen(offer->orderTx, submitexthtlc.offerTx)) - return Res::Err("offerTx (%s) has expired", submitexthtlc.offerTx.GetHex()); + Require(HasAuth(order->ownerAddress), "tx must have at least one input from order owner"); + Require(mnview.HasICXMakeOfferOpen(offer->orderTx, submitexthtlc.offerTx), + "offerTx (%s) has expired", + submitexthtlc.offerTx.GetHex()); uint32_t timeout; if (static_cast(height) < consensus.EunosPayaHeight) @@ -2750,38 +2468,28 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { else timeout = CICXSubmitEXTHTLC::EUNOSPAYA_MINIMUM_TIMEOUT; - if (submitexthtlc.timeout < timeout) - return Res::Err("timeout must be greater than %d", timeout - 1); + Require(submitexthtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - CAmount calcAmount(static_cast( - (arith_uint256(submitexthtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)) - .GetLow64())); - if (calcAmount > offer->amount) - return Res::Err("amount must be lower or equal the offer one"); + auto calcAmount = MultiplyAmounts(submitexthtlc.amount, order->orderPrice); + Require(calcAmount <= offer->amount, "amount must be lower or equal the offer one"); CAmount takerFee = offer->takerFee; // EunosPaya: calculating adjusted takerFee only if amount in htlc different than in offer if (static_cast(height) >= consensus.EunosPayaHeight) { if (calcAmount < offer->amount) { - CAmount BTCAmount(static_cast( - (arith_uint256(offer->amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)) - .GetLow64())); - takerFee = static_cast((arith_uint256(submitexthtlc.amount) * - arith_uint256(offer->takerFee) / arith_uint256(BTCAmount)) - .GetLow64()); + auto BTCAmount = DivideAmounts(offer->amount, order->orderPrice); + takerFee = (arith_uint256(submitexthtlc.amount) * offer->takerFee / BTCAmount).GetLow64(); } - } else { + } else takerFee = CalculateTakerFee(submitexthtlc.amount); - } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { CalculateOwnerRewards(offer->ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress); - if (!res) - return res; + Require( + TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress)); // update the offer with adjusted takerFee offer->takerFee = takerFee; @@ -2789,22 +2497,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // burn takerFee - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress)); // burn makerDeposit CalculateOwnerRewards(order->ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress); + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress)); } - return !res ? res : mnview.ICXSubmitEXTHTLC(submitexthtlc); + return mnview.ICXSubmitEXTHTLC(submitexthtlc); } Res operator()(const CICXClaimDFCHTLCMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXClaimDFCHTLCImplemetation claimdfchtlc; static_cast(claimdfchtlc) = obj; @@ -2813,93 +2517,73 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { claimdfchtlc.creationHeight = height; auto dfchtlc = mnview.GetICXSubmitDFCHTLCByCreationTx(claimdfchtlc.dfchtlcTx); - if (!dfchtlc) - return Res::Err("dfc htlc with creation tx %s does not exists!", claimdfchtlc.dfchtlcTx.GetHex()); + Require(dfchtlc, "dfc htlc with creation tx %s does not exists!", claimdfchtlc.dfchtlcTx.GetHex()); - if (!mnview.HasICXSubmitDFCHTLCOpen(dfchtlc->offerTx)) - return Res::Err("dfc htlc not found or already claimed or refunded!"); + Require(mnview.HasICXSubmitDFCHTLCOpen(dfchtlc->offerTx), "dfc htlc not found or already claimed or refunded!"); uint256 calcHash; uint8_t calcSeedBytes[32]; CSHA256().Write(claimdfchtlc.seed.data(), claimdfchtlc.seed.size()).Finalize(calcSeedBytes); calcHash.SetHex(HexStr(calcSeedBytes, calcSeedBytes + 32)); - if (dfchtlc->hash != calcHash) - return Res::Err("hash generated from given seed is different than in dfc htlc: %s - %s!", - calcHash.GetHex(), - dfchtlc->hash.GetHex()); + Require(dfchtlc->hash == calcHash, + "hash generated from given seed is different than in dfc htlc: %s - %s!", + calcHash.GetHex(), + dfchtlc->hash.GetHex()); auto offer = mnview.GetICXMakeOfferByCreationTx(dfchtlc->offerTx); - if (!offer) - return Res::Err("offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); + Require(offer, "offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - if (!order) - return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); auto exthtlc = mnview.HasICXSubmitEXTHTLCOpen(dfchtlc->offerTx); - if (static_cast(height) < consensus.EunosPayaHeight && !exthtlc) - return Res::Err("cannot claim, external htlc for this offer does not exists or expired!"); + if (static_cast(height) < consensus.EunosPayaHeight) + Require(exthtlc, "cannot claim, external htlc for this offer does not exists or expired!"); // claim DFC HTLC to receiveAddress CalculateOwnerRewards(order->ownerAddress); CScript htlcTxidAddr(dfchtlc->creationTx.begin(), dfchtlc->creationTx.end()); - if (order->orderType == CICXOrder::TYPE_INTERNAL) { - CalculateOwnerRewards(offer->ownerAddress); - res = TransferTokenBalance(order->idToken, dfchtlc->amount, htlcTxidAddr, offer->ownerAddress); - } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) - res = TransferTokenBalance(order->idToken, dfchtlc->amount, htlcTxidAddr, order->ownerAddress); - if (!res) - return res; + + if (order->orderType == CICXOrder::TYPE_INTERNAL) + Require(TransferTokenBalance(order->idToken, dfchtlc->amount, htlcTxidAddr, offer->ownerAddress)); + else if (order->orderType == CICXOrder::TYPE_EXTERNAL) + Require(TransferTokenBalance(order->idToken, dfchtlc->amount, htlcTxidAddr, order->ownerAddress)); // refund makerDeposit - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, CScript(), order->ownerAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, CScript(), order->ownerAddress)); // makerIncentive - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee * 25 / 100, CScript(), order->ownerAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee * 25 / 100, CScript(), order->ownerAddress)); // maker bonus only on fair dBTC/BTC (1:1) trades for now DCT_ID BTC = FindTokenByPartialSymbolName(CICXOrder::TOKEN_BTC); if (order->idToken == BTC && order->orderPrice == COIN) { if ((IsTestNetwork() && height >= 1250000) || Params().NetworkIDString() == CBaseChainParams::REGTEST) { - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee * 50 / 100, CScript(), order->ownerAddress); + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee * 50 / 100, CScript(), order->ownerAddress)); } else { - res = TransferTokenBalance(BTC, offer->takerFee * 50 / 100, CScript(), order->ownerAddress); + Require(TransferTokenBalance(BTC, offer->takerFee * 50 / 100, CScript(), order->ownerAddress)); } - if (!res) - return res; } if (order->orderType == CICXOrder::TYPE_INTERNAL) order->amountToFill -= dfchtlc->amount; else if (order->orderType == CICXOrder::TYPE_EXTERNAL) - order->amountToFill -= static_cast( - (arith_uint256(dfchtlc->amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)).GetLow64()); + order->amountToFill -= DivideAmounts(dfchtlc->amount, order->orderPrice); // Order fulfilled, close order. if (order->amountToFill == 0) { order->closeTx = claimdfchtlc.creationTx; order->closeHeight = height; - res = mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_FILLED); - if (!res) - return res; + Require(mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_FILLED)); } - res = mnview.ICXClaimDFCHTLC(claimdfchtlc, offer->creationTx, *order); - if (!res) - return res; + Require(mnview.ICXClaimDFCHTLC(claimdfchtlc, offer->creationTx, *order)); // Close offer - res = mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); - if (!res) - return res; - res = mnview.ICXCloseDFCHTLC(*dfchtlc, CICXSubmitDFCHTLC::STATUS_CLAIMED); - if (!res) - return res; + Require(mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED)); + + Require(mnview.ICXCloseDFCHTLC(*dfchtlc, CICXSubmitDFCHTLC::STATUS_CLAIMED)); if (static_cast(height) >= consensus.EunosPayaHeight) { if (exthtlc) @@ -2911,9 +2595,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CICXCloseOrderMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXCloseOrderImplemetation closeorder; static_cast(closeorder) = obj; @@ -2921,19 +2603,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { closeorder.creationTx = tx.GetHash(); closeorder.creationHeight = height; - std::unique_ptr order; - if (!(order = mnview.GetICXOrderByCreationTx(closeorder.orderTx))) - return Res::Err("order with creation tx %s does not exists!", closeorder.orderTx.GetHex()); - - if (!order->closeTx.IsNull()) - return Res::Err("order with creation tx %s is already closed!", closeorder.orderTx.GetHex()); + auto order = mnview.GetICXOrderByCreationTx(closeorder.orderTx); + Require(order, "order with creation tx %s does not exists!", closeorder.orderTx.GetHex()); - if (!mnview.HasICXOrderOpen(order->idToken, order->creationTx)) - return Res::Err("order with creation tx %s is already closed!", closeorder.orderTx.GetHex()); + Require(order->closeTx.IsNull(), "order with creation tx %s is already closed!", closeorder.orderTx.GetHex()); + Require(mnview.HasICXOrderOpen(order->idToken, order->creationTx), + "order with creation tx %s is already closed!", + closeorder.orderTx.GetHex()); // check auth - if (!HasAuth(order->ownerAddress)) - return Res::Err("tx must have at least one input from order owner"); + Require(HasAuth(order->ownerAddress), "tx must have at least one input from order owner"); order->closeTx = closeorder.creationTx; order->closeHeight = closeorder.creationHeight; @@ -2942,19 +2621,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // subtract the balance from txidAddr and return to owner CScript txidAddr(order->creationTx.begin(), order->creationTx.end()); CalculateOwnerRewards(order->ownerAddress); - res = TransferTokenBalance(order->idToken, order->amountToFill, txidAddr, order->ownerAddress); - if (!res) - return res; + Require(TransferTokenBalance(order->idToken, order->amountToFill, txidAddr, order->ownerAddress)); } - res = mnview.ICXCloseOrder(closeorder); - return !res ? res : mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_CLOSED); + Require(mnview.ICXCloseOrder(closeorder)); + return mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_CLOSED); } Res operator()(const CICXCloseOfferMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); CICXCloseOfferImplemetation closeoffer; static_cast(closeoffer) = obj; @@ -2962,23 +2637,19 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { closeoffer.creationTx = tx.GetHash(); closeoffer.creationHeight = height; - std::unique_ptr offer; - if (!(offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx))) - return Res::Err("offer with creation tx %s does not exists!", closeoffer.offerTx.GetHex()); + auto offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx); + Require(offer, "offer with creation tx %s does not exists!", closeoffer.offerTx.GetHex()); - if (!offer->closeTx.IsNull()) - return Res::Err("offer with creation tx %s is already closed!", closeoffer.offerTx.GetHex()); + Require(offer->closeTx.IsNull(), "offer with creation tx %s is already closed!", closeoffer.offerTx.GetHex()); + Require(mnview.HasICXMakeOfferOpen(offer->orderTx, offer->creationTx), + "offer with creation tx %s does not exists!", + closeoffer.offerTx.GetHex()); - if (!mnview.HasICXMakeOfferOpen(offer->orderTx, offer->creationTx)) - return Res::Err("offer with creation tx %s does not exists!", closeoffer.offerTx.GetHex()); - - std::unique_ptr order; - if (!(order = mnview.GetICXOrderByCreationTx(offer->orderTx))) - return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); + Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); // check auth - if (!HasAuth(offer->ownerAddress)) - return Res::Err("tx must have at least one input from offer owner"); + Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); offer->closeTx = closeoffer.creationTx; offer->closeHeight = closeoffer.creationHeight; @@ -2990,36 +2661,26 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // subtract takerFee from txidAddr and return to owner CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); CalculateOwnerRewards(offer->ownerAddress); - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress); - if (!res) - return res; + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress)); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { // subtract the balance from txidAddr and return to owner CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); CalculateOwnerRewards(offer->ownerAddress); - if (isPreEunosPaya) { - res = TransferTokenBalance(order->idToken, offer->amount, txidAddr, offer->ownerAddress); - if (!res) - return res; - } - if (!mnview.ExistedICXSubmitEXTHTLC(offer->creationTx, isPreEunosPaya)) { - res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress); - if (!res) - return res; - } + if (isPreEunosPaya) + Require(TransferTokenBalance(order->idToken, offer->amount, txidAddr, offer->ownerAddress)); + + if (!mnview.ExistedICXSubmitEXTHTLC(offer->creationTx, isPreEunosPaya)) + Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress)); } - res = mnview.ICXCloseOffer(closeoffer); - return !res ? res : mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); + Require(mnview.ICXCloseOffer(closeoffer)); + return mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); } Res operator()(const CLoanSetCollateralTokenMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); - if (!HasFoundationAuth()) - return Res::Err("tx not from foundation member!"); + Require(HasFoundationAuth(), "tx not from foundation member!"); if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &tokenId = obj.idToken.v; @@ -3032,30 +2693,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, tokenId, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - if (!gv) { - return Res::Err("Failed to create ATTRIBUTES Governance variable"); - } + Require(gv, "Failed to create ATTRIBUTES Governance variable"); auto var = std::dynamic_pointer_cast(gv); - if (!var) { - return Res::Err("Failed to convert ATTRIBUTES Governance variable"); - } + Require(var, "Failed to convert ATTRIBUTES Governance variable"); var->SetValue(collateralEnabled, true); var->SetValue(collateralFactor, obj.factor); var->SetValue(pairKey, obj.fixedIntervalPriceId); - res = attributes->Import(var->Export()); - if (!res) - return res; - - res = attributes->Validate(mnview); - if (!res) - return res; - - res = attributes->Apply(mnview, height); - if (!res) - return res; + Require(attributes->Import(var->Export())); + Require(attributes->Validate(mnview)); + Require(attributes->Apply(mnview, height)); return mnview.SetVariable(*attributes); } @@ -3067,48 +2716,41 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { collToken.creationHeight = height; auto token = mnview.GetToken(collToken.idToken); - if (!token) - return Res::Err("token %s does not exist!", collToken.idToken.ToString()); + Require(token, "token %s does not exist!", collToken.idToken.ToString()); if (!collToken.activateAfterBlock) collToken.activateAfterBlock = height; - if (collToken.activateAfterBlock < height) - return Res::Err("activateAfterBlock cannot be less than current height!"); + Require(collToken.activateAfterBlock >= height, "activateAfterBlock cannot be less than current height!"); - if (!OraclePriceFeed(mnview, collToken.fixedIntervalPriceId)) - return Res::Err("Price feed %s/%s does not belong to any oracle", - collToken.fixedIntervalPriceId.first, - collToken.fixedIntervalPriceId.second); + Require(OraclePriceFeed(mnview, collToken.fixedIntervalPriceId), + "Price feed %s/%s does not belong to any oracle", + collToken.fixedIntervalPriceId.first, + collToken.fixedIntervalPriceId.second); CFixedIntervalPrice fixedIntervalPrice; fixedIntervalPrice.priceFeedId = collToken.fixedIntervalPriceId; auto price = GetAggregatePrice( mnview, collToken.fixedIntervalPriceId.first, collToken.fixedIntervalPriceId.second, time); - if (!price) - return Res::Err(price.msg); + Require(price, price.msg); fixedIntervalPrice.priceRecord[1] = price; fixedIntervalPrice.timestamp = time; auto resSetFixedPrice = mnview.SetFixedIntervalPrice(fixedIntervalPrice); - if (!resSetFixedPrice) - return Res::Err(resSetFixedPrice.msg); + Require(resSetFixedPrice, resSetFixedPrice.msg); return mnview.CreateLoanCollateralToken(collToken); } Res operator()(const CLoanSetLoanTokenMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); - if (!HasFoundationAuth()) - return Res::Err("tx not from foundation member!"); + Require(HasFoundationAuth(), "tx not from foundation member!"); - if (obj.interest < 0 && height < static_cast(consensus.FortCanningGreatWorldHeight)) { - return Res::Err("interest rate cannot be less than 0!"); + if (height < static_cast(consensus.FortCanningGreatWorldHeight)) { + Require(obj.interest >= 0, "interest rate cannot be less than 0!"); } CTokenImplementation token; @@ -3122,8 +2764,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { static_cast(CToken::TokenFlags::LoanToken) | static_cast(CToken::TokenFlags::DAT); auto tokenId = mnview.CreateToken(token); - if (!tokenId) - return std::move(tokenId); + Require(tokenId); if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &id = tokenId.val->v; @@ -3136,31 +2777,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, id, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - if (!gv) { - return Res::Err("Failed to create ATTRIBUTES Governance variable"); - } + Require(gv, "Failed to create ATTRIBUTES Governance variable"); auto var = std::dynamic_pointer_cast(gv); - if (!var) { - return Res::Err("Failed to convert ATTRIBUTES Governance variable"); - } + Require(var, "Failed to convert ATTRIBUTES Governance variable"); var->SetValue(mintEnabled, obj.mintable); var->SetValue(mintInterest, obj.interest); var->SetValue(pairKey, obj.fixedIntervalPriceId); - res = attributes->Import(var->Export()); - if (!res) - return res; - - res = attributes->Validate(mnview); - if (!res) - return res; - - res = attributes->Apply(mnview, height); - if (!res) - return res; - + Require(attributes->Import(var->Export())); + Require(attributes->Validate(mnview)); + Require(attributes->Apply(mnview, height)); return mnview.SetVariable(*attributes); } @@ -3172,13 +2800,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { auto nextPrice = GetAggregatePrice(mnview, obj.fixedIntervalPriceId.first, obj.fixedIntervalPriceId.second, time); - if (!nextPrice) - return Res::Err(nextPrice.msg); + Require(nextPrice, nextPrice.msg); - if (!OraclePriceFeed(mnview, obj.fixedIntervalPriceId)) - return Res::Err("Price feed %s/%s does not belong to any oracle", - obj.fixedIntervalPriceId.first, - obj.fixedIntervalPriceId.second); + Require(OraclePriceFeed(mnview, obj.fixedIntervalPriceId), + "Price feed %s/%s does not belong to any oracle", + obj.fixedIntervalPriceId.first, + obj.fixedIntervalPriceId.second); CFixedIntervalPrice fixedIntervalPrice; fixedIntervalPrice.priceFeedId = loanToken.fixedIntervalPriceId; @@ -3186,35 +2813,29 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { fixedIntervalPrice.timestamp = time; auto resSetFixedPrice = mnview.SetFixedIntervalPrice(fixedIntervalPrice); - if (!resSetFixedPrice) - return Res::Err(resSetFixedPrice.msg); + Require(resSetFixedPrice, resSetFixedPrice.msg); return mnview.SetLoanToken(loanToken, *(tokenId.val)); } Res operator()(const CLoanUpdateLoanTokenMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); - if (!HasFoundationAuth()) - return Res::Err("tx not from foundation member!"); + Require(HasFoundationAuth(), "tx not from foundation member!"); - if (obj.interest < 0 && height < static_cast(consensus.FortCanningGreatWorldHeight)) { - return Res::Err("interest rate cannot be less than 0!"); + if (height < static_cast(consensus.FortCanningGreatWorldHeight)) { + Require(obj.interest >= 0, "interest rate cannot be less than 0!"); } auto pair = mnview.GetTokenByCreationTx(obj.tokenTx); - if (!pair) - return Res::Err("Loan token (%s) does not exist!", obj.tokenTx.GetHex()); + Require(pair, "Loan token (%s) does not exist!", obj.tokenTx.GetHex()); auto loanToken = (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) ? mnview.GetLoanTokenByID(pair->first) : mnview.GetLoanToken(obj.tokenTx); - if (!loanToken) - return Res::Err("Loan token (%s) does not exist!", obj.tokenTx.GetHex()); + Require(loanToken, "Loan token (%s) does not exist!", obj.tokenTx.GetHex()); if (obj.mintable != loanToken->mintable) loanToken->mintable = obj.mintable; @@ -3231,9 +2852,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (obj.mintable != (pair->second.flags & (uint8_t)CToken::TokenFlags::Mintable)) pair->second.flags ^= (uint8_t)CToken::TokenFlags::Mintable; - res = mnview.UpdateToken(pair->second); - if (!res) - return res; + Require(mnview.UpdateToken(pair->second)); if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &id = pair->first.v; @@ -3246,39 +2865,26 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, id, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - if (!gv) { - return Res::Err("Failed to create ATTRIBUTES Governance variable"); - } + Require(gv, "Failed to create ATTRIBUTES Governance variable"); auto var = std::dynamic_pointer_cast(gv); - if (!var) { - return Res::Err("Failed to convert ATTRIBUTES Governance variable"); - } + Require(var, "Failed to convert ATTRIBUTES Governance variable"); var->SetValue(mintEnabled, obj.mintable); var->SetValue(mintInterest, obj.interest); var->SetValue(pairKey, obj.fixedIntervalPriceId); - res = attributes->Import(var->Export()); - if (!res) - return res; - - res = attributes->Validate(mnview); - if (!res) - return res; - - res = attributes->Apply(mnview, height); - if (!res) - return res; - + Require(attributes->Import(var->Export())); + Require(attributes->Validate(mnview)); + Require(attributes->Apply(mnview, height)); return mnview.SetVariable(*attributes); } if (obj.fixedIntervalPriceId != loanToken->fixedIntervalPriceId) { - if (!OraclePriceFeed(mnview, obj.fixedIntervalPriceId)) - return Res::Err("Price feed %s/%s does not belong to any oracle", - obj.fixedIntervalPriceId.first, - obj.fixedIntervalPriceId.second); + Require(OraclePriceFeed(mnview, obj.fixedIntervalPriceId), + "Price feed %s/%s does not belong to any oracle", + obj.fixedIntervalPriceId.first, + obj.fixedIntervalPriceId.second); loanToken->fixedIntervalPriceId = obj.fixedIntervalPriceId; } @@ -3287,26 +2893,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CLoanSchemeMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) { - return res; - } + Require(CheckCustomTx()); - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member!"); - } + Require(HasFoundationAuth(), "tx not from foundation member!"); - if (obj.ratio < 100) { - return Res::Err("minimum collateral ratio cannot be less than 100"); - } + Require(obj.ratio >= 100, "minimum collateral ratio cannot be less than 100"); - if (obj.rate < 1000000) { - return Res::Err("interest rate cannot be less than 0.01"); - } + Require(obj.rate >= 1000000, "interest rate cannot be less than 0.01"); - if (obj.identifier.empty() || obj.identifier.length() > 8) { - return Res::Err("id cannot be empty or more than 8 chars long"); - } + Require(!obj.identifier.empty() && obj.identifier.length() <= 8, + "id cannot be empty or more than 8 chars long"); // Look for loan scheme which already has matching rate and ratio bool duplicateLoan = false; @@ -3321,45 +2917,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { return true; }); - if (duplicateLoan) { - return Res::Err("Loan scheme %s with same interestrate and mincolratio already exists", duplicateID); - } else { - // Look for delayed loan scheme which already has matching rate and ratio - std::pair duplicateKey; - mnview.ForEachDelayedLoanScheme( - [&](const std::pair &key, const CLoanSchemeMessage &data) { - // Duplicate delayed loan scheme - if (data.ratio == obj.ratio && data.rate == obj.rate) { - duplicateLoan = true; - duplicateKey = key; - return false; - } - return true; - }); + Require(!duplicateLoan, "Loan scheme %s with same interestrate and mincolratio already exists", duplicateID); - if (duplicateLoan) { - return Res::Err("Loan scheme %s with same interestrate and mincolratio pending on block %d", - duplicateKey.first, - duplicateKey.second); - } - } + // Look for delayed loan scheme which already has matching rate and ratio + std::pair duplicateKey; + mnview.ForEachDelayedLoanScheme( + [&](const std::pair &key, const CLoanSchemeMessage &data) { + // Duplicate delayed loan scheme + if (data.ratio == obj.ratio && data.rate == obj.rate) { + duplicateLoan = true; + duplicateKey = key; + return false; + } + return true; + }); + + Require(!duplicateLoan, + "Loan scheme %s with same interestrate and mincolratio pending on block %d", + duplicateKey.first, + duplicateKey.second); // New loan scheme, no duplicate expected. - if (mnview.GetLoanScheme(obj.identifier)) { - if (!obj.updateHeight) { - return Res::Err("Loan scheme already exist with id %s", obj.identifier); - } - } else if (obj.updateHeight) { - return Res::Err("Cannot find existing loan scheme with id %s", obj.identifier); - } + if (mnview.GetLoanScheme(obj.identifier)) + Require(obj.updateHeight, "Loan scheme already exist with id %s", obj.identifier); + else + Require(!obj.updateHeight, "Cannot find existing loan scheme with id %s", obj.identifier); // Update set, not max uint64_t which indicates immediate update and not updated on this block. if (obj.updateHeight && obj.updateHeight != std::numeric_limits::max() && obj.updateHeight != height) { - if (obj.updateHeight < height) { - return Res::Err("Update height below current block height, set future height"); - } - + Require(obj.updateHeight >= height, "Update height below current block height, set future height"); return mnview.StoreDelayedLoanScheme(obj); } @@ -3372,64 +2959,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CDefaultLoanSchemeMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) { - return res; - } + Require(CheckCustomTx()); + Require(HasFoundationAuth(), "tx not from foundation member!"); - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member!"); - } - - if (obj.identifier.empty() || obj.identifier.length() > 8) { - return Res::Err("id cannot be empty or more than 8 chars long"); - } - - if (!mnview.GetLoanScheme(obj.identifier)) { - return Res::Err("Cannot find existing loan scheme with id %s", obj.identifier); - } - - const auto currentID = mnview.GetDefaultLoanScheme(); - if (currentID && *currentID == obj.identifier) { - return Res::Err("Loan scheme with id %s is already set as default", obj.identifier); - } + Require(!obj.identifier.empty() && obj.identifier.length() <= 8, + "id cannot be empty or more than 8 chars long"); + Require(mnview.GetLoanScheme(obj.identifier), "Cannot find existing loan scheme with id %s", obj.identifier); - if (auto height = mnview.GetDestroyLoanScheme(obj.identifier)) { - return Res::Err("Cannot set %s as default, set to destroyed on block %d", obj.identifier, *height); - } + if (auto currentID = mnview.GetDefaultLoanScheme()) + Require(*currentID != obj.identifier, "Loan scheme with id %s is already set as default", obj.identifier); + const auto height = mnview.GetDestroyLoanScheme(obj.identifier); + Require(!height, "Cannot set %s as default, set to destroyed on block %d", obj.identifier, *height); return mnview.StoreDefaultLoanScheme(obj.identifier); - ; } Res operator()(const CDestroyLoanSchemeMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) { - return res; - } - - if (!HasFoundationAuth()) { - return Res::Err("tx not from foundation member!"); - } + Require(CheckCustomTx()); - if (obj.identifier.empty() || obj.identifier.length() > 8) { - return Res::Err("id cannot be empty or more than 8 chars long"); - } + Require(HasFoundationAuth(), "tx not from foundation member!"); - if (!mnview.GetLoanScheme(obj.identifier)) { - return Res::Err("Cannot find existing loan scheme with id %s", obj.identifier); - } + Require(!obj.identifier.empty() && obj.identifier.length() <= 8, + "id cannot be empty or more than 8 chars long"); + Require(mnview.GetLoanScheme(obj.identifier), "Cannot find existing loan scheme with id %s", obj.identifier); const auto currentID = mnview.GetDefaultLoanScheme(); - if (currentID && *currentID == obj.identifier) { - return Res::Err("Cannot destroy default loan scheme, set new default first"); - } + Require(currentID && *currentID != obj.identifier, "Cannot destroy default loan scheme, set new default first"); // Update set and not updated on this block. if (obj.destroyHeight && obj.destroyHeight != height) { - if (obj.destroyHeight < height) { - return Res::Err("Destruction height below current block height, set future height"); - } + Require(obj.destroyHeight >= height, "Destruction height below current block height, set future height"); return mnview.StoreDelayedDestroyScheme(obj); } @@ -3446,65 +3005,52 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CVaultMessage &obj) const { auto vaultCreationFee = consensus.vaultCreationFee; - if (tx.vout[0].nValue != vaultCreationFee || tx.vout[0].nTokenId != DCT_ID{0}) { - return Res::Err("malformed tx vouts, creation vault fee is %s DFI", GetDecimaleString(vaultCreationFee)); - } + Require(tx.vout[0].nValue == vaultCreationFee && tx.vout[0].nTokenId == DCT_ID{0}, + "Malformed tx vouts, creation vault fee is %s DFI", + GetDecimaleString(vaultCreationFee)); CVaultData vault{}; static_cast(vault) = obj; // set loan scheme to default if non provided if (obj.schemeId.empty()) { - if (auto defaultScheme = mnview.GetDefaultLoanScheme()) { - vault.schemeId = *defaultScheme; - } else { - return Res::Err("There is no default loan scheme"); - } + auto defaultScheme = mnview.GetDefaultLoanScheme(); + Require(defaultScheme, "There is no default loan scheme"); + vault.schemeId = *defaultScheme; } // loan scheme exists - if (!mnview.GetLoanScheme(vault.schemeId)) { - return Res::Err("Cannot find existing loan scheme with id %s", vault.schemeId); - } + Require(mnview.GetLoanScheme(vault.schemeId), "Cannot find existing loan scheme with id %s", vault.schemeId); // check loan scheme is not to be destroyed - if (auto height = mnview.GetDestroyLoanScheme(obj.schemeId)) { - return Res::Err("Cannot set %s as loan scheme, set to be destroyed on block %d", obj.schemeId, *height); - } + auto height = mnview.GetDestroyLoanScheme(obj.schemeId); + Require(!height, "Cannot set %s as loan scheme, set to be destroyed on block %d", obj.schemeId, *height); auto vaultId = tx.GetHash(); return mnview.StoreVault(vaultId, vault); } Res operator()(const CCloseVaultMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // vault exists auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (vault->isUnderLiquidation) - return Res::Err("Cannot close vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot close vault under liquidation"); // owner auth - if (!HasAuth(vault->ownerAddress)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); if (const auto loans = mnview.GetLoanTokens(obj.vaultId)) { for (const auto &[tokenId, amount] : loans->balances) { const auto rate = mnview.GetInterestRate(obj.vaultId, tokenId, height); - if (!rate) { - return Res::Err("Cannot get interest rate for this token (%d)", tokenId.v); - } + Require(rate, "Cannot get interest rate for this token (%d)", tokenId.v); const auto totalInterest = TotalInterest(*rate, height); - if (amount + totalInterest > 0) { - return Res::Err("Vault <%s> has loans", obj.vaultId.GetHex()); - } + + Require(amount + totalInterest <= 0, "Vault <%s> has loans", obj.vaultId.GetHex()); if (totalInterest < 0) { TrackNegativeInterest( @@ -3514,54 +3060,45 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } CalculateOwnerRewards(obj.to); - if (auto collaterals = mnview.GetVaultCollaterals(obj.vaultId)) { - for (const auto &col : collaterals->balances) { - auto res = mnview.AddBalance(obj.to, {col.first, col.second}); - if (!res) - return res; - } - } + if (auto collaterals = mnview.GetVaultCollaterals(obj.vaultId)) + for (const auto &col : collaterals->balances) + Require(mnview.AddBalance(obj.to, {col.first, col.second})); // delete all interest to vault - res = mnview.EraseInterest(obj.vaultId, height); - if (!res) - return res; + Require(mnview.EraseInterest(obj.vaultId, height)); // return half fee, the rest is burned at creation auto feeBack = consensus.vaultCreationFee / 2; - res = mnview.AddBalance(obj.to, {DCT_ID{0}, feeBack}); - return !res ? res : mnview.EraseVault(obj.vaultId); + Require(mnview.AddBalance(obj.to, {DCT_ID{0}, feeBack})); + return mnview.EraseVault(obj.vaultId); } Res operator()(const CUpdateVaultMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // vault exists auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (vault->isUnderLiquidation) - return Res::Err("Cannot update vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot update vault under liquidation"); // owner auth - if (!HasAuth(vault->ownerAddress)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); // loan scheme exists const auto scheme = mnview.GetLoanScheme(obj.schemeId); - if (!scheme) - return Res::Err("Cannot find existing loan scheme with id %s", obj.schemeId); + Require(scheme, "Cannot find existing loan scheme with id %s", obj.schemeId); // loan scheme is not set to be destroyed - if (auto height = mnview.GetDestroyLoanScheme(obj.schemeId)) - return Res::Err("Cannot set %s as loan scheme, set to be destroyed on block %d", obj.schemeId, *height); + auto destroyHeight = mnview.GetDestroyLoanScheme(obj.schemeId); + Require(!destroyHeight, + "Cannot set %s as loan scheme, set to be destroyed on block %d", + obj.schemeId, + *destroyHeight); - if (!IsVaultPriceValid(mnview, obj.vaultId, height)) - return Res::Err("Cannot update vault while any of the asset's price is invalid"); + Require(IsVaultPriceValid(mnview, obj.vaultId, height), + "Cannot update vault while any of the asset's price is invalid"); // don't allow scheme change when vault is going to be in liquidation if (vault->schemeId != obj.schemeId) { @@ -3570,11 +3107,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { bool useNextPrice = i > 0, requireLivePrice = true; auto collateralsLoans = mnview.GetLoanCollaterals( obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - if (!collateralsLoans) - return std::move(collateralsLoans); + Require(collateralsLoans); - if (collateralsLoans.val->ratio() < scheme->ratio) - return Res::Err( + Require(collateralsLoans.val->ratio() >= scheme->ratio, "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", collateralsLoans.val->ratio(), scheme->ratio); @@ -3585,11 +3120,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (const auto &[tokenId, tokenAmount] : loanTokens->balances) { const auto loanToken = mnview.GetLoanTokenByID(tokenId); assert(loanToken); - res = - mnview.IncreaseInterest(height, obj.vaultId, obj.schemeId, tokenId, loanToken->interest, 0); - if (!res) { - return res; - } + Require(mnview.IncreaseInterest( + height, obj.vaultId, obj.schemeId, tokenId, loanToken->interest, 0)); } } } @@ -3603,7 +3135,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res CollateralPctCheck(const bool hasDUSDLoans, const CCollateralLoans &collateralsLoans, const uint32_t ratio) const { - std::optional>> tokenDUSD; + std::optional > > tokenDUSD; if (static_cast(height) >= consensus.FortCanningRoadHeight) { tokenDUSD = mnview.GetToken("DUSD"); } @@ -3676,88 +3208,74 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CDepositToVaultMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // owner auth - if (!HasAuth(obj.from)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(obj.from), "tx must have at least one input from token owner"); // vault exists auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (vault->isUnderLiquidation) - return Res::Err("Cannot deposit to vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot deposit to vault under liquidation"); // If collateral token exist make sure it is enabled. if (mnview.GetCollateralTokenFromAttributes(obj.amount.nTokenId)) { CDataStructureV0 collateralKey{ AttributeTypes::Token, obj.amount.nTokenId.v, TokenKeys::LoanCollateralEnabled}; - if (const auto attributes = mnview.GetAttributes(); !attributes->GetValue(collateralKey, false)) { - return Res::Err("Collateral token (%d) is disabled", obj.amount.nTokenId.v); + if (const auto attributes = mnview.GetAttributes()) { + Require(attributes->GetValue(collateralKey, false), + "Collateral token (%d) is disabled", + obj.amount.nTokenId.v); } } // check balance CalculateOwnerRewards(obj.from); - res = mnview.SubBalance(obj.from, obj.amount); - if (!res) - return Res::Err( - "Insufficient funds: can't subtract balance of %s: %s\n", ScriptToString(obj.from), res.msg); + Require(mnview.SubBalance(obj.from, obj.amount), [&](const std::string &msg) { + return strprintf("Insufficient funds: can't subtract balance of %s: %s\n", ScriptToString(obj.from), msg); + }); - res = mnview.AddVaultCollateral(obj.vaultId, obj.amount); - if (!res) - return res; + Require(mnview.AddVaultCollateral(obj.vaultId, obj.amount)); bool useNextPrice = false, requireLivePrice = false; auto collaterals = mnview.GetVaultCollaterals(obj.vaultId); auto collateralsLoans = mnview.GetLoanCollaterals(obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - if (!collateralsLoans) - return std::move(collateralsLoans); + Require(collateralsLoans); auto scheme = mnview.GetLoanScheme(vault->schemeId); - if (collateralsLoans.val->ratio() < scheme->ratio) - return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + Require(collateralsLoans.val->ratio() >= scheme->ratio, + "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", + collateralsLoans.val->ratio(), + scheme->ratio); return Res::Ok(); } Res operator()(const CWithdrawFromVaultMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // vault exists auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (vault->isUnderLiquidation) - return Res::Err("Cannot withdraw from vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot withdraw from vault under liquidation"); // owner auth - if (!HasAuth(vault->ownerAddress)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); - if (!IsVaultPriceValid(mnview, obj.vaultId, height)) - return Res::Err("Cannot withdraw from vault while any of the asset's price is invalid"); + Require(IsVaultPriceValid(mnview, obj.vaultId, height), + "Cannot withdraw from vault while any of the asset's price is invalid"); - res = mnview.SubVaultCollateral(obj.vaultId, obj.amount); - if (!res) - return res; + Require(mnview.SubVaultCollateral(obj.vaultId, obj.amount)); auto hasDUSDLoans = false; - std::optional>> tokenDUSD; + std::optional > > tokenDUSD; if (static_cast(height) >= consensus.FortCanningRoadHeight) { tokenDUSD = mnview.GetToken("DUSD"); } @@ -3786,10 +3304,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {tokenId, subAmount}); } - res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount}); - if (!res) { - return res; - } + Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount})); TrackNegativeInterest(mnview, {tokenId, subAmount}); @@ -3801,21 +3316,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (int i = 0; i < 2; i++) { // check collaterals for active and next price bool useNextPrice = i > 0, requireLivePrice = true; - auto collateralsLoans = mnview.GetLoanCollaterals( obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - if (!collateralsLoans) - return std::move(collateralsLoans); + Require(collateralsLoans); - if (collateralsLoans.val->ratio() < scheme->ratio) - return Res::Err( + Require(collateralsLoans.val->ratio() >= scheme->ratio, "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", collateralsLoans.val->ratio(), scheme->ratio); - res = CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio); - if (!res) - return res; + Require(CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio)); } } else { return Res::Err("Cannot withdraw all collaterals as there are still active loans in this vault"); @@ -3826,71 +3336,59 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CPaybackWithCollateralMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // vault exists const auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (vault->isUnderLiquidation) - return Res::Err("Cannot payback vault with collateral while vault's under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot payback vault with collateral while vault's under liquidation"); // owner auth - if (!HasAuth(vault->ownerAddress)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); return PaybackWithCollateral(mnview, *vault, obj.vaultId, height, time); } Res operator()(const CLoanTakeLoanMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); const auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); - if (vault->isUnderLiquidation) - return Res::Err("Cannot take loan on vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot take loan on vault under liquidation"); // vault owner auth - if (!HasAuth(vault->ownerAddress)) - return Res::Err("tx must have at least one input from vault owner"); + Require(HasAuth(vault->ownerAddress), "tx must have at least one input from vault owner"); - if (!IsVaultPriceValid(mnview, obj.vaultId, height)) - return Res::Err("Cannot take loan while any of the asset's price in the vault is not live"); + Require(IsVaultPriceValid(mnview, obj.vaultId, height), + "Cannot take loan while any of the asset's price in the vault is not live"); auto collaterals = mnview.GetVaultCollaterals(obj.vaultId); - if (!collaterals) - return Res::Err("Vault with id %s has no collaterals", obj.vaultId.GetHex()); + Require(collaterals, "Vault with id %s has no collaterals", obj.vaultId.GetHex()); const auto loanAmounts = mnview.GetLoanTokens(obj.vaultId); auto hasDUSDLoans = false; - std::optional>> tokenDUSD; + std::optional > > tokenDUSD; if (static_cast(height) >= consensus.FortCanningRoadHeight) { tokenDUSD = mnview.GetToken("DUSD"); } uint64_t totalLoansActivePrice = 0, totalLoansNextPrice = 0; for (const auto &[tokenId, tokenAmount] : obj.amounts.balances) { - if (height >= static_cast(consensus.FortCanningGreatWorldHeight) && tokenAmount <= 0) - return Res::Err("Valid loan amount required (input: %d@%d)", tokenAmount, tokenId.v); + if (height >= static_cast(consensus.FortCanningGreatWorldHeight)) { + Require(tokenAmount > 0, "Valid loan amount required (input: %d@%d)", tokenAmount, tokenId.v); + } auto loanToken = mnview.GetLoanTokenByID(tokenId); - if (!loanToken) - return Res::Err("Loan token with id (%s) does not exist!", tokenId.ToString()); - - if (!loanToken->mintable) - return Res::Err("Loan cannot be taken on token with id (%s) as \"mintable\" is currently false", - tokenId.ToString()); + Require(loanToken, "Loan token with id (%s) does not exist!", tokenId.ToString()); + Require(loanToken->mintable, + "Loan cannot be taken on token with id (%s) as \"mintable\" is currently false", + tokenId.ToString()); if (tokenDUSD && tokenId == tokenDUSD->first) { hasDUSDLoans = true; } @@ -3931,9 +3429,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDAdd(mnview, {tokenId, loanAmountChange}); } - res = mnview.AddLoanToken(obj.vaultId, CTokenAmount{tokenId, loanAmountChange}); - if (!res) - return res; + Require(mnview.AddLoanToken(obj.vaultId, CTokenAmount{tokenId, loanAmountChange})); } else { const auto subAmount = currentLoanAmount > std::abs(loanAmountChange) ? std::abs(loanAmountChange) : currentLoanAmount; @@ -3942,74 +3438,63 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {tokenId, subAmount}); } - res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount}); - if (!res) { - return res; - } + Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount})); } if (resetInterestToHeight) { mnview.ResetInterest(height, obj.vaultId, vault->schemeId, tokenId); } else { - res = mnview.IncreaseInterest( - height, obj.vaultId, vault->schemeId, tokenId, loanToken->interest, loanAmountChange); - if (!res) - return res; + Require(mnview.IncreaseInterest( + height, obj.vaultId, vault->schemeId, tokenId, loanToken->interest, loanAmountChange)); } const auto tokenCurrency = loanToken->fixedIntervalPriceId; auto priceFeed = mnview.GetFixedIntervalPrice(tokenCurrency); - if (!priceFeed) - return Res::Err(priceFeed.msg); + Require(priceFeed, priceFeed.msg); - if (!priceFeed.val->isLive(mnview.GetPriceDeviation())) - return Res::Err("No live fixed prices for %s/%s", tokenCurrency.first, tokenCurrency.second); + Require(priceFeed.val->isLive(mnview.GetPriceDeviation()), + "No live fixed prices for %s/%s", + tokenCurrency.first, + tokenCurrency.second); for (int i = 0; i < 2; i++) { // check active and next price auto price = priceFeed.val->priceRecord[int(i > 0)]; auto amount = MultiplyAmounts(price, tokenAmount); - if (price > COIN && amount < tokenAmount) - return Res::Err( - "Value/price too high (%s/%s)", GetDecimaleString(tokenAmount), GetDecimaleString(price)); - + if (price > COIN) { + Require(amount >= tokenAmount, + "Value/price too high (%s/%s)", + GetDecimaleString(tokenAmount), + GetDecimaleString(price)); + } auto &totalLoans = i > 0 ? totalLoansNextPrice : totalLoansActivePrice; auto prevLoans = totalLoans; totalLoans += amount; - if (prevLoans > totalLoans) - return Res::Err("Exceed maximum loans"); + Require(prevLoans <= totalLoans, "Exceed maximum loans"); } - res = mnview.AddMintedTokens(tokenId, tokenAmount); - if (!res) - return res; + Require(mnview.AddMintedTokens(tokenId, tokenAmount)); const auto &address = !obj.to.empty() ? obj.to : vault->ownerAddress; CalculateOwnerRewards(address); - res = mnview.AddBalance(address, CTokenAmount{tokenId, tokenAmount}); - if (!res) - return res; + Require(mnview.AddBalance(address, CTokenAmount{tokenId, tokenAmount})); } auto scheme = mnview.GetLoanScheme(vault->schemeId); for (int i = 0; i < 2; i++) { // check ratio against current and active price bool useNextPrice = i > 0, requireLivePrice = true; - auto collateralsLoans = mnview.GetLoanCollaterals(obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - if (!collateralsLoans) - return std::move(collateralsLoans); + Require(collateralsLoans); - if (collateralsLoans.val->ratio() < scheme->ratio) - return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + Require(collateralsLoans.val->ratio() >= scheme->ratio, + "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", + collateralsLoans.val->ratio(), + scheme->ratio); - res = CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio); - if (!res) - return res; + Require(CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio)); } return Res::Ok(); } @@ -4023,8 +3508,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CBalances *loan; if (id == DCT_ID{0}) { auto tokenDUSD = mnview.GetToken("DUSD"); - if (!tokenDUSD) - return Res::Err("Loan token DUSD does not exist!"); + Require(tokenDUSD, "Loan token DUSD does not exist!"); loan = &loans[tokenDUSD->first]; } else loan = &loans[id]; @@ -4035,27 +3519,21 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CLoanPaybackLoanV2Message &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); const auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Cannot find existing vault with id %s", obj.vaultId.GetHex()); + Require(vault, "Cannot find existing vault with id %s", obj.vaultId.GetHex()); - if (vault->isUnderLiquidation) - return Res::Err("Cannot payback loan on vault under liquidation"); + Require(!vault->isUnderLiquidation, "Cannot payback loan on vault under liquidation"); - if (!mnview.GetVaultCollaterals(obj.vaultId)) { - return Res::Err("Vault with id %s has no collaterals", obj.vaultId.GetHex()); - } + Require(mnview.GetVaultCollaterals(obj.vaultId), "Vault with id %s has no collaterals", obj.vaultId.GetHex()); - if (!HasAuth(obj.from)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(obj.from), "tx must have at least one input from token owner"); - if (static_cast(height) < consensus.FortCanningRoadHeight && - !IsVaultPriceValid(mnview, obj.vaultId, height)) - return Res::Err("Cannot payback loan while any of the asset's price is invalid"); + if (static_cast(height) < consensus.FortCanningRoadHeight) { + Require(IsVaultPriceValid(mnview, obj.vaultId, height), + "Cannot payback loan while any of the asset's price is invalid"); + } // Handle payback with collateral special case if (static_cast(height) >= consensus.FortCanningEpilogueHeight && @@ -4069,46 +3547,45 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (const auto &[loanTokenId, paybackAmounts] : obj.loans) { const auto loanToken = mnview.GetLoanTokenByID(loanTokenId); - if (!loanToken) - return Res::Err("Loan token with id (%s) does not exist!", loanTokenId.ToString()); + Require(loanToken, "Loan token with id (%s) does not exist!", loanTokenId.ToString()); for (const auto &kv : paybackAmounts.balances) { const auto &paybackTokenId = kv.first; auto paybackAmount = kv.second; - if (height >= static_cast(consensus.FortCanningGreatWorldHeight) && paybackAmount <= 0) { - return Res::Err("Valid payback amount required (input: %d@%d)", paybackAmount, paybackTokenId.v); + if (height >= static_cast(consensus.FortCanningGreatWorldHeight)) { + Require(paybackAmount > 0, + "Valid payback amount required (input: %d@%d)", + paybackAmount, + paybackTokenId.v); } CAmount paybackUsdPrice{0}, loanUsdPrice{0}, penaltyPct{COIN}; auto paybackToken = mnview.GetToken(paybackTokenId); - if (!paybackToken) - return Res::Err("Token with id (%s) does not exists", paybackTokenId.ToString()); + Require(paybackToken, "Token with id (%s) does not exists", paybackTokenId.ToString()); if (loanTokenId != paybackTokenId) { - if (!IsVaultPriceValid(mnview, obj.vaultId, height)) - return Res::Err("Cannot payback loan while any of the asset's price is invalid"); - - if (!attributes) - return Res::Err("Payback is not currently active"); + 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"); // search in token to token if (paybackTokenId != DCT_ID{0}) { CDataStructureV0 activeKey{ AttributeTypes::Token, loanTokenId.v, TokenKeys::LoanPayback, paybackTokenId.v}; - if (!attributes->GetValue(activeKey, false)) - return Res::Err("Payback of loan via %s token is not currently active", - paybackToken->symbol); + Require(attributes->GetValue(activeKey, false), + "Payback of loan via %s token is not currently active", + 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}; - if (!attributes->GetValue(activeKey, false)) - return Res::Err("Payback of loan via %s token is not currently active", - paybackToken->symbol); + Require(attributes->GetValue(activeKey, false), + "Payback of loan via %s token is not currently active", + paybackToken->symbol); CDataStructureV0 penaltyKey{AttributeTypes::Token, loanTokenId.v, TokenKeys::PaybackDFIFeePCT}; penaltyPct -= attributes->GetValue(penaltyKey, COIN / 100); @@ -4118,8 +3595,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const CTokenCurrencyPair tokenUsdPair{paybackToken->symbol, "USD"}; bool useNextPrice{false}, requireLivePrice{true}; const auto resVal = mnview.GetValidatedIntervalPrice(tokenUsdPair, useNextPrice, requireLivePrice); - if (!resVal) - return std::move(resVal); + Require(resVal); paybackUsdPrice = MultiplyAmounts(*resVal.val, penaltyPct); @@ -4128,18 +3604,19 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (loanToken->symbol == "DUSD") { paybackAmount = usdAmount; - if (paybackUsdPrice > COIN && paybackAmount < kv.second) - return Res::Err("Value/price too high (%s/%s)", - GetDecimaleString(kv.second), - GetDecimaleString(paybackUsdPrice)); + if (paybackUsdPrice > COIN) { + Require(paybackAmount >= kv.second, + "Value/price too high (%s/%s)", + GetDecimaleString(kv.second), + GetDecimaleString(paybackUsdPrice)); + } } else { // Get dToken price in USD const CTokenCurrencyPair dTokenUsdPair{loanToken->symbol, "USD"}; bool useNextPrice{false}, requireLivePrice{true}; const auto resVal = mnview.GetValidatedIntervalPrice(dTokenUsdPair, useNextPrice, requireLivePrice); - if (!resVal) - return std::move(resVal); + Require(resVal); loanUsdPrice = *resVal.val; @@ -4148,17 +3625,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } const auto loanAmounts = mnview.GetLoanTokens(obj.vaultId); - if (!loanAmounts) - return Res::Err("There are no loans on this vault (%s)!", obj.vaultId.GetHex()); + Require(loanAmounts, "There are no loans on this vault (%s)!", obj.vaultId.GetHex()); - if (!loanAmounts->balances.count(loanTokenId)) - return Res::Err("There is no loan on token (%s) in this vault!", loanToken->symbol); + Require(loanAmounts->balances.count(loanTokenId), + "There is no loan on token (%s) in this vault!", + loanToken->symbol); const auto ¤tLoanAmount = loanAmounts->balances.at(loanTokenId); const auto rate = mnview.GetInterestRate(obj.vaultId, loanTokenId, height); - if (!rate) - return Res::Err("Cannot get interest rate for this token (%s)!", loanToken->symbol); + Require(rate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); auto subInterest = TotalInterest(*rate, height); @@ -4182,14 +3658,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {loanTokenId, subLoan}); } - res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan}); - if (!res) - return res; + Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan})); // 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. - res = mnview.DecreaseInterest( + Require(mnview.DecreaseInterest( height, obj.vaultId, vault->schemeId, @@ -4197,18 +3671,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { subLoan, subInterest < 0 || (rate->interestPerBlock.negative && subLoan == currentLoanAmount) ? std::numeric_limits::max() - : subInterest); - if (!res) - return res; + : subInterest)); if (height >= static_cast(consensus.FortCanningMuseumHeight) && subLoan < currentLoanAmount && height < static_cast(consensus.FortCanningGreatWorldHeight)) { auto newRate = mnview.GetInterestRate(obj.vaultId, loanTokenId, height); - if (!newRate) - return Res::Err("Cannot get interest rate for this token (%s)!", loanToken->symbol); + Require(newRate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); - if (newRate->interestPerBlock.amount == 0) - return Res::Err( + Require(newRate->interestPerBlock.amount != 0, "Cannot payback this amount of loan for %s, either payback full amount or less than this " "amount!", loanToken->symbol); @@ -4217,9 +3687,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CalculateOwnerRewards(obj.from); if (paybackTokenId == loanTokenId) { - res = mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest); - if (!res) - return res; + Require(mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest)); // If interest was negative remove it from sub amount if (height >= static_cast(consensus.FortCanningEpilogueHeight) && subInterest < 0) @@ -4238,9 +3706,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { "CLoanPaybackLoanMessage(): Sub loan from balance - %lld, height - %d\n", subLoan, height); - res = mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan}); - if (!res) - return res; + Require(mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan})); } // burn interest Token->USD->DFI->burnAddress @@ -4250,8 +3716,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { loanToken->symbol, subInterest, height); - res = - SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height); + Require( + SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height)); } } else { CAmount subInToken; @@ -4300,7 +3766,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { paybackToken->symbol, height); - res = TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress); + Require(TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress)); } else { CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackTokens}; auto balances = attributes->GetValue(liveKey, CTokenPayback{}); @@ -4322,18 +3788,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { AttributeTypes::Param, ParamIDs::DFIP2206A, DFIPKeys::DUSDLoanBurn}; auto directLoanBurn = attributes->GetValue(directBurnKey, false); - res = SwapToDFIorDUSD(mnview, - paybackTokenId, - subInToken, - obj.from, - consensus.burnAddress, - height, - !directLoanBurn); + Require(SwapToDFIorDUSD(mnview, + paybackTokenId, + subInToken, + obj.from, + consensus.burnAddress, + height, + !directLoanBurn)); } } - - if (!res) - return res; } } @@ -4341,48 +3804,39 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CAuctionBidMessage &obj) const { - auto res = CheckCustomTx(); - if (!res) - return res; + Require(CheckCustomTx()); // owner auth - if (!HasAuth(obj.from)) - return Res::Err("tx must have at least one input from token owner"); + Require(HasAuth(obj.from), "tx must have at least one input from token owner"); // vault exists auto vault = mnview.GetVault(obj.vaultId); - if (!vault) - return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - if (!vault->isUnderLiquidation) - return Res::Err("Cannot bid to vault which is not under liquidation"); + Require(vault->isUnderLiquidation, "Cannot bid to vault which is not under liquidation"); auto data = mnview.GetAuction(obj.vaultId, height); - if (!data) - return Res::Err("No auction data to vault %s", obj.vaultId.GetHex()); + Require(data, "No auction data to vault %s", obj.vaultId.GetHex()); auto batch = mnview.GetAuctionBatch({obj.vaultId, obj.index}); - if (!batch) - return Res::Err("No batch to vault/index %s/%d", obj.vaultId.GetHex(), obj.index); + Require(batch, "No batch to vault/index %s/%d", obj.vaultId.GetHex(), obj.index); - if (obj.amount.nTokenId != batch->loanAmount.nTokenId) - return Res::Err("Bid token does not match auction one"); + Require(obj.amount.nTokenId == batch->loanAmount.nTokenId, "Bid token does not match auction one"); auto bid = mnview.GetAuctionBid({obj.vaultId, obj.index}); if (!bid) { auto amount = MultiplyAmounts(batch->loanAmount.nValue, COIN + data->liquidationPenalty); - if (amount > obj.amount.nValue) - return Res::Err("First bid should include liquidation penalty of %d%%", - data->liquidationPenalty * 100 / COIN); + Require(amount <= obj.amount.nValue, + "First bid should include liquidation penalty of %d%%", + data->liquidationPenalty * 100 / COIN); if (static_cast(height) >= consensus.FortCanningMuseumHeight && data->liquidationPenalty && obj.amount.nValue == batch->loanAmount.nValue) return Res::Err("First bid should be higher than batch one"); } else { auto amount = MultiplyAmounts(bid->second.nValue, COIN + (COIN / 100)); - if (amount > obj.amount.nValue) - return Res::Err("Bid override should be at least 1%% higher than current one"); + Require(amount <= obj.amount.nValue, "Bid override should be at least 1%% higher than current one"); if (static_cast(height) >= consensus.FortCanningMuseumHeight && obj.amount.nValue == bid->second.nValue) @@ -4394,8 +3848,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // check balance CalculateOwnerRewards(obj.from); - res = mnview.SubBalance(obj.from, obj.amount); - return !res ? res : mnview.StoreAuctionBid({obj.vaultId, obj.index}, {obj.from, obj.amount}); + Require(mnview.SubBalance(obj.from, obj.amount)); + return mnview.StoreAuctionBid({obj.vaultId, obj.index}, {obj.from, obj.amount}); } Res operator()(const CCreatePropMessage &obj) const { @@ -4694,6 +4148,7 @@ Res ApplyCustomTx(CCustomCSView &mnview, if (metadataValidation && txType == CustomTxType::Reject) { return Res::ErrCode(CustomTxErrCodes::Fatal, "Invalid custom transaction"); } + auto txMessage = customTypeToMessage(txType); CAccountsHistoryWriter view(mnview, height, txn, tx.GetHash(), uint8_t(txType), writers); if ((res = CustomMetadataParse(height, consensus, metadata, txMessage))) { @@ -4777,9 +4232,7 @@ ResVal ApplyAnchorRewardTx(CCustomCSView &mnview, const uint256 &prevStakeModifier, const std::vector &metadata, const Consensus::Params &consensusParams) { - if (height >= consensusParams.DakotaHeight) { - return Res::Err("Old anchor TX type after Dakota fork. Height %d", height); - } + Require(height < consensusParams.DakotaHeight, "Old anchor TX type after Dakota fork. Height %d", height); CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); CAnchorFinalizationMessage finMsg; @@ -4858,9 +4311,7 @@ ResVal ApplyAnchorRewardTxPlus(CCustomCSView &mnview, int height, const std::vector &metadata, const Consensus::Params &consensusParams) { - if (height < consensusParams.DakotaHeight) { - return Res::Err("New anchor TX type before Dakota fork. Height %d", height); - } + Require(height >= consensusParams.DakotaHeight, "New anchor TX type before Dakota fork. Height %d", height); CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); CAnchorFinalizationMessagePlus finMsg; @@ -4887,49 +4338,33 @@ ResVal ApplyAnchorRewardTxPlus(CCustomCSView &mnview, } auto quorum = GetMinAnchorQuorum(*team); - if (finMsg.sigs.size() < quorum) { - return Res::ErrDbg("bad-ar-sigs-quorum", "anchor sigs (%d) < min quorum (%) ", finMsg.sigs.size(), quorum); - } - - if (uniqueKeys < quorum) { - return Res::ErrDbg("bad-ar-sigs-quorum", "anchor unique keys (%d) < min quorum (%) ", uniqueKeys, quorum); - } + Require(finMsg.sigs.size() >= quorum, "anchor sigs (%d) < min quorum (%) ", finMsg.sigs.size(), quorum); + Require(uniqueKeys >= quorum, "anchor unique keys (%d) < min quorum (%) ", uniqueKeys, quorum); // Make sure anchor block height and hash exist in chain. - CBlockIndex *anchorIndex = ::ChainActive()[finMsg.anchorHeight]; - if (!anchorIndex) { - return Res::ErrDbg("bad-ar-height", - "Active chain does not contain block height %d. Chain height %d", - finMsg.anchorHeight, - ::ChainActive().Height()); - } - - if (anchorIndex->GetBlockHash() != finMsg.dfiBlockHash) { - return Res::ErrDbg("bad-ar-hash", - "Anchor and blockchain mismatch at height %d. Expected %s found %s", - finMsg.anchorHeight, - anchorIndex->GetBlockHash().ToString(), - finMsg.dfiBlockHash.ToString()); - } - + auto *anchorIndex = ::ChainActive()[finMsg.anchorHeight]; + Require(anchorIndex, + "Active chain does not contain block height %d. Chain height %d", + finMsg.anchorHeight, + ::ChainActive().Height()); + Require(anchorIndex->GetBlockHash() == finMsg.dfiBlockHash, + "Anchor and blockchain mismatch at height %d. Expected %s found %s", + finMsg.anchorHeight, + anchorIndex->GetBlockHash().ToString(), + finMsg.dfiBlockHash.ToString()); // check reward sum const auto cbValues = tx.GetValuesOut(); - if (cbValues.size() != 1 || cbValues.begin()->first != DCT_ID{0}) - return Res::ErrDbg("bad-ar-wrong-tokens", "anchor reward should be paid in DFI only"); + Require(cbValues.size() == 1 && cbValues.begin()->first == DCT_ID{0}, "anchor reward should be paid in DFI only"); const auto anchorReward = mnview.GetCommunityBalance(CommunityAccountType::AnchorReward); - if (cbValues.begin()->second != anchorReward) { - return Res::ErrDbg("bad-ar-amount", - "anchor pays wrong amount (actual=%d vs expected=%d)", - cbValues.begin()->second, - anchorReward); - } + Require(cbValues.begin()->second == anchorReward, + "anchor pays wrong amount (actual=%d vs expected=%d)", + cbValues.begin()->second, + anchorReward); CTxDestination destination = finMsg.rewardKeyType == 1 ? CTxDestination(PKHash(finMsg.rewardKeyID)) : CTxDestination(WitnessV0KeyHash(finMsg.rewardKeyID)); - if (tx.vout[1].scriptPubKey != GetScriptForDestination(destination)) { - return Res::ErrDbg("bad-ar-dest", "anchor pay destination is incorrect"); - } + Require(tx.vout[1].scriptPubKey == GetScriptForDestination(destination), "anchor pay destination is incorrect"); LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: txid=%s fund=%s change=%s\n", @@ -4956,7 +4391,7 @@ bool IsMempooledCustomTxCreate(const CTxMemPool &pool, const uint256 &txid) { } std::vector CPoolSwap::CalculateSwaps(CCustomCSView &view, bool testOnly) { - std::vector> poolPaths = CalculatePoolPaths(view); + std::vector > poolPaths = CalculatePoolPaths(view); // Record best pair std::pair, CAmount> bestPair{{}, -1}; @@ -4986,8 +4421,8 @@ std::vector CPoolSwap::CalculateSwaps(CCustomCSView &view, bool testOnly return bestPair.first; } -std::vector> CPoolSwap::CalculatePoolPaths(CCustomCSView &view) { - std::vector> poolPaths; +std::vector > CPoolSwap::CalculatePoolPaths(CCustomCSView &view) { + std::vector > poolPaths; // For tokens to be traded get all pairs and pool IDs std::multimap fromPoolsID, toPoolsID; @@ -5057,6 +4492,7 @@ std::vector> CPoolSwap::CalculatePoolPaths(CCustomCSView &vi } } } + return true; }, {0}); @@ -5070,15 +4506,12 @@ std::vector> CPoolSwap::CalculatePoolPaths(CCustomCSView &vi // testOnly is only meant for one-off tests per well defined view. Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, bool testOnly) { Res poolResult = Res::Ok(); - // No composite swap allowed before Fort Canning if (height < static_cast(Params().GetConsensus().FortCanningHeight) && !poolIDs.empty()) { poolIDs.clear(); } - if (obj.amountFrom <= 0) { - return Res::Err("Input amount should be positive"); - } + Require(obj.amountFrom > 0, "Input amount should be positive"); if (height >= static_cast(Params().GetConsensus().FortCanningHillHeight) && poolIDs.size() > MAX_POOL_SWAPS) { @@ -5088,12 +4521,10 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo // Single swap if no pool IDs provided auto poolPrice = POOLPRICE_MAX; - std::optional> poolPair; + std::optional > poolPair; if (poolIDs.empty()) { poolPair = view.GetPoolPair(obj.idTokenFrom, obj.idTokenTo); - if (!poolPair) { - return Res::Err("Cannot find the pool pair."); - } + Require(poolPair, "Cannot find the pool pair."); // Add single swap pool to vector for loop poolIDs.push_back(poolPair->first); @@ -5129,9 +4560,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo } else // Or get pools from IDs provided for composite swap { pool = view.GetPoolPair(currentID); - if (!pool) { - return Res::Err("Cannot find the pool pair."); - } + Require(pool, "Cannot find the pool pair."); } // Check if last pool swap @@ -5140,12 +4569,11 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo const auto swapAmount = swapAmountResult; if (height >= static_cast(Params().GetConsensus().FortCanningHillHeight) && lastSwap) { - if (obj.idTokenTo == swapAmount.nTokenId) { - return Res::Err("Final swap should have idTokenTo as destination, not source"); - } - if (pool->idTokenA != obj.idTokenTo && pool->idTokenB != obj.idTokenTo) { - return Res::Err("Final swap pool should have idTokenTo, incorrect final pool ID provided"); - } + Require(obj.idTokenTo != swapAmount.nTokenId, + "Final swap should have idTokenTo as destination, not source"); + + Require(pool->idTokenA == obj.idTokenTo || pool->idTokenB == obj.idTokenTo, + "Final swap pool should have idTokenTo, incorrect final pool ID provided"); } if (view.AreTokensLocked({pool->idTokenA.v, pool->idTokenB.v})) { @@ -5281,7 +4709,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo // Assign to result for loop testing best pool swap result result = swapAmountResult.nValue; - return poolResult; + return Res::Ok(); } Res SwapToDFIorDUSD(CCustomCSView &mnview, @@ -5302,18 +4730,14 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, auto poolSwap = CPoolSwap(obj, height); auto token = mnview.GetToken(tokenId); - if (!token) - return Res::Err("Cannot find token with id %s!", tokenId.ToString()); + Require(token, "Cannot find token with id %s!", tokenId.ToString()); // TODO: Optimize double look up later when first token is DUSD. auto dUsdToken = mnview.GetToken("DUSD"); - if (!dUsdToken) - return Res::Err("Cannot find token DUSD"); + Require(dUsdToken, "Cannot find token DUSD"); const auto attributes = mnview.GetAttributes(); - if (!attributes) { - return Res::Err("Attributes unavailable"); - } + Require(attributes, "Attributes unavailable"); CDataStructureV0 directBurnKey{AttributeTypes::Param, ParamIDs::DFIP2206A, DFIPKeys::DUSDInterestBurn}; // Direct swap from DUSD to DFI as defined in the CPoolSwapMessage. @@ -5322,9 +4746,7 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, // direct burn dUSD CTokenAmount dUSD{dUsdToken->first, amount}; - auto res = mnview.SubBalance(from, dUSD); - if (!res) - return res; + Require(mnview.SubBalance(from, dUSD)); return mnview.AddBalance(to, dUSD); } else @@ -5333,12 +4755,10 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, } auto pooldUSDDFI = mnview.GetPoolPair(dUsdToken->first, DCT_ID{0}); - if (!pooldUSDDFI) - return Res::Err("Cannot find pool pair DUSD-DFI!"); + Require(pooldUSDDFI, "Cannot find pool pair DUSD-DFI!"); auto poolTokendUSD = mnview.GetPoolPair(tokenId, dUsdToken->first); - if (!poolTokendUSD) - return Res::Err("Cannot find pool pair %s-DUSD!", token->symbol); + Require(poolTokendUSD, "Cannot find pool pair %s-DUSD!", token->symbol); if (to == Params().GetConsensus().burnAddress && !forceLoanSwap && attributes->GetValue(directBurnKey, false)) { obj.idTokenTo = dUsdToken->first; @@ -5405,38 +4825,30 @@ Res PaybackWithCollateral(CCustomCSView &view, uint32_t height, uint64_t time) { const auto attributes = view.GetAttributes(); - if (!attributes) - return Res::Err("Attributes unavailable"); + Require(attributes, "Attributes unavailable"); const auto dUsdToken = view.GetToken("DUSD"); - if (!dUsdToken) - return Res::Err("Cannot find token DUSD"); + Require(dUsdToken, "Cannot find token DUSD"); CDataStructureV0 activeKey{AttributeTypes::Token, dUsdToken->first.v, TokenKeys::LoanPaybackCollateral}; - if (!attributes->GetValue(activeKey, false)) - return Res::Err("Payback of DUSD loan with collateral is not currently active"); + Require(attributes->GetValue(activeKey, false), "Payback of DUSD loan with collateral is not currently active"); const auto collateralAmounts = view.GetVaultCollaterals(vaultId); - if (!collateralAmounts) { - return Res::Err("Vault has no collaterals"); - } - if (!collateralAmounts->balances.count(dUsdToken->first)) { - return Res::Err("Vault does not have any DUSD collaterals"); - } + Require(collateralAmounts, "Vault has no collaterals"); + + Require(collateralAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD collaterals"); + const auto &collateralDUSD = collateralAmounts->balances.at(dUsdToken->first); const auto loanAmounts = view.GetLoanTokens(vaultId); - if (!loanAmounts) { - return Res::Err("Vault has no loans"); - } - if (!loanAmounts->balances.count(dUsdToken->first)) { - return Res::Err("Vault does not have any DUSD loans"); - } + Require(loanAmounts, "Vault has no loans"); + + Require(loanAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD loans"); + const auto &loanDUSD = loanAmounts->balances.at(dUsdToken->first); const auto rate = view.GetInterestRate(vaultId, dUsdToken->first, height); - if (!rate) - return Res::Err("Cannot get interest rate for this token (DUSD)!"); + Require(rate, "Cannot get interest rate for this token (DUSD)!"); const auto subInterest = TotalInterest(*rate, height); Res res{}; @@ -5448,13 +4860,9 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subInterest > collateralDUSD) { subCollateralAmount = collateralDUSD; - res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); - if (!res) - return res; + Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); - res = view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount); - if (!res) - return res; + Require(view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount)); burnAmount = subCollateralAmount; } else { @@ -5471,15 +4879,11 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subLoanAmount > 0) { TrackDUSDSub(view, {dUsdToken->first, subLoanAmount}); - res = view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount}); - if (!res) - return res; + Require(view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount})); } if (subCollateralAmount > 0) { - res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); - if (!res) - return res; + Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); } view.ResetInterest(height, vaultId, vault.schemeId, dUsdToken->first); @@ -5487,9 +4891,7 @@ Res PaybackWithCollateral(CCustomCSView &view, } if (burnAmount > 0) { - res = view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount}); - if (!res) - return res; + Require(view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount})); } else { TrackNegativeInterest(view, {dUsdToken->first, std::abs(burnAmount)}); } @@ -5497,29 +4899,26 @@ Res PaybackWithCollateral(CCustomCSView &view, // Guard against liquidation const auto collaterals = view.GetVaultCollaterals(vaultId); const auto loans = view.GetLoanTokens(vaultId); - if (!collaterals && loans) - return Res::Err("Vault cannot have loans without collaterals"); + if (loans) + Require(collaterals, "Vault cannot have loans without collaterals"); auto collateralsLoans = view.GetLoanCollaterals(vaultId, *collaterals, height, time); - if (!collateralsLoans) - return std::move(collateralsLoans); + Require(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. - if (!IsVaultPriceValid(view, vaultId, height)) - return Res::Err("Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); + Require(IsVaultPriceValid(view, vaultId, height), + "Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); const auto scheme = view.GetLoanScheme(vault.schemeId); - if (collateralsLoans.val->ratio() < scheme->ratio) - return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + 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 (subCollateralAmount > 0) { - res = view.SubMintedTokens(dUsdToken->first, subCollateralAmount); - if (!res) - return res; + Require(view.SubMintedTokens(dUsdToken->first, subCollateralAmount)); } return Res::Ok(); diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 39c1e76442b..140d9732336 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -36,7 +36,7 @@ class CCustomTxVisitor { const Consensus::Params &consensus); protected: - bool HasAuth(const CScript &auth) const; + Res HasAuth(const CScript &auth) const; Res HasCollateralAuth(const uint256 &collateralTx) const; Res HasFoundationAuth() const; Res CheckMasternodeCreationTx() const; diff --git a/src/masternodes/oracles.cpp b/src/masternodes/oracles.cpp index 84641fd01ca..d7aef45a500 100644 --- a/src/masternodes/oracles.cpp +++ b/src/masternodes/oracles.cpp @@ -13,9 +13,7 @@ bool COracle::SupportsPair(const std::string &token, const std::string ¤cy } Res COracle::SetTokenPrice(const std::string &token, const std::string ¤cy, CAmount amount, int64_t timestamp) { - if (!SupportsPair(token, currency)) { - return Res::Err("token <%s> - currency <%s> is not allowed", token, currency); - } + Require(SupportsPair(token, currency), "token <%s> - currency <%s> is not allowed", token, currency); tokenPrices[token][currency] = std::make_pair(amount, timestamp); @@ -23,30 +21,22 @@ Res COracle::SetTokenPrice(const std::string &token, const std::string ¤cy } ResVal COracle::GetTokenPrice(const std::string &token, const std::string ¤cy) { - if (!SupportsPair(token, currency)) { - return Res::Err("token <%s> - currency <%s> is not allowed", token, currency); - } + Require(SupportsPair(token, currency), "token <%s> - currency <%s> is not allowed", token, currency); return ResVal(tokenPrices[token][currency].first, Res::Ok()); } Res COracleView::AppointOracle(const COracleId &oracleId, const COracle &oracle) { - if (!WriteBy(oracleId, oracle)) { - return Res::Err("failed to appoint the new oracle <%s>", oracleId.GetHex()); - } + Require(WriteBy(oracleId, oracle), "failed to appoint the new oracle <%s>", oracleId.GetHex()); return Res::Ok(); } Res COracleView::UpdateOracle(const COracleId &oracleId, COracle &&newOracle) { COracle oracle; - if (!ReadBy(oracleId, oracle)) { - return Res::Err("oracle <%s> not found", oracleId.GetHex()); - } + Require(ReadBy(oracleId, oracle), "oracle <%s> not found", oracleId.GetHex()); - if (!newOracle.tokenPrices.empty()) { - return Res::Err("oracle <%s> has token prices on update", oracleId.GetHex()); - } + Require(newOracle.tokenPrices.empty(), "oracle <%s> has token prices on update", oracleId.GetHex()); oracle.weightage = newOracle.weightage; oracle.oracleAddress = std::move(newOracle.oracleAddress); @@ -67,55 +57,39 @@ Res COracleView::UpdateOracle(const COracleId &oracleId, COracle &&newOracle) { oracle.availablePairs = std::move(newOracle.availablePairs); // no need to update oracles list - if (!WriteBy(oracleId, oracle)) { - return Res::Err("failed to save oracle <%s>", oracleId.GetHex()); - } + Require(WriteBy(oracleId, oracle), "failed to save oracle <%s>", oracleId.GetHex()); return Res::Ok(); } Res COracleView::RemoveOracle(const COracleId &oracleId) { - if (!ExistsBy(oracleId)) { - return Res::Err("oracle <%s> not found", oracleId.GetHex()); - } + Require(ExistsBy(oracleId), "oracle <%s> not found", oracleId.GetHex()); // remove oracle - if (!EraseBy(oracleId)) { - return Res::Err("failed to remove oracle <%s>", oracleId.GetHex()); - } + Require(EraseBy(oracleId), "failed to remove oracle <%s>", oracleId.GetHex()); return Res::Ok(); } Res COracleView::SetOracleData(const COracleId &oracleId, int64_t timestamp, const CTokenPrices &tokenPrices) { COracle oracle; - if (!ReadBy(oracleId, oracle)) { - return Res::Err("failed to read oracle %s from database", oracleId.GetHex()); - } + Require(ReadBy(oracleId, oracle), "failed to read oracle %s from database", oracleId.GetHex()); for (const auto &tokenPrice : tokenPrices) { const auto &token = tokenPrice.first; for (const auto &price : tokenPrice.second) { const auto ¤cy = price.first; - auto res = oracle.SetTokenPrice(token, currency, price.second, timestamp); - if (!res.ok) { - return res; - } + Require(oracle.SetTokenPrice(token, currency, price.second, timestamp)); } } - if (!WriteBy(oracleId, oracle)) { - return Res::Err("failed to store oracle %s to database", oracleId.GetHex()); - } - + Require(WriteBy(oracleId, oracle), "failed to store oracle %s to database", oracleId.GetHex()); return Res::Ok(); } ResVal COracleView::GetOracleData(const COracleId &oracleId) const { COracle oracle; - if (!ReadBy(oracleId, oracle)) { - return Res::Err("oracle <%s> not found", oracleId.GetHex()); - } + Require(ReadBy(oracleId, oracle), "oracle <%s> not found", oracleId.GetHex()); return ResVal(oracle, Res::Ok()); } @@ -131,11 +105,11 @@ bool CFixedIntervalPrice::isLive(const CAmount deviationThreshold) const { } Res COracleView::SetFixedIntervalPrice(const CFixedIntervalPrice &fixedIntervalPrice) { - if (!WriteBy(fixedIntervalPrice.priceFeedId, fixedIntervalPrice)) { - return Res::Err("failed to set new price feed <%s/%s>", - fixedIntervalPrice.priceFeedId.first, - fixedIntervalPrice.priceFeedId.second); - } + Require(WriteBy(fixedIntervalPrice.priceFeedId, fixedIntervalPrice), + "failed to set new price feed <%s/%s>", + fixedIntervalPrice.priceFeedId.first, + fixedIntervalPrice.priceFeedId.second); + LogPrint(BCLog::ORACLE, "%s(): %s/%s, active - %lld, next - %lld\n", __func__, @@ -149,10 +123,10 @@ Res COracleView::SetFixedIntervalPrice(const CFixedIntervalPrice &fixedIntervalP ResVal COracleView::GetFixedIntervalPrice(const CTokenCurrencyPair &fixedIntervalPriceId) { CFixedIntervalPrice fixedIntervalPrice; - if (!ReadBy(fixedIntervalPriceId, fixedIntervalPrice)) { - return Res::Err( - "fixedIntervalPrice with id <%s/%s> not found", fixedIntervalPriceId.first, fixedIntervalPriceId.second); - } + Require(ReadBy(fixedIntervalPriceId, fixedIntervalPrice), + "fixedIntervalPrice with id <%s/%s> not found", + fixedIntervalPriceId.first, + fixedIntervalPriceId.second); DCT_ID firstID{}, secondID{}; const auto firstToken = GetTokenGuessId(fixedIntervalPriceId.first, firstID); @@ -167,9 +141,7 @@ ResVal COracleView::GetFixedIntervalPrice(const CTokenCurre loanTokens.insert(secondID.v); } - if (AreTokensLocked(loanTokens)) { - return Res::Err("Fixed interval price currently disabled due to locked token"); - } + Require(!AreTokensLocked(loanTokens), "Fixed interval price currently disabled due to locked token"); LogPrint(BCLog::ORACLE, "%s(): %s/%s, active - %lld, next - %lld\n", diff --git a/src/masternodes/poolpairs.cpp b/src/masternodes/poolpairs.cpp index 961331f05b0..b5954e17019 100644 --- a/src/masternodes/poolpairs.cpp +++ b/src/masternodes/poolpairs.cpp @@ -69,16 +69,12 @@ ReturnType ReadValueAt(CPoolPairView *poolView, const PoolHeightKey &poolKey) { } Res CPoolPairView::SetPoolPair(DCT_ID const &poolId, uint32_t height, const CPoolPair &pool) { - if (pool.idTokenA == pool.idTokenB) { - return Res::Err("Error: tokens IDs are the same."); - } - + Require(pool.idTokenA != pool.idTokenB, "Error: tokens IDs are the same."); auto poolPairByID = GetPoolPair(poolId); auto poolIdByTokens = ReadBy(ByPairKey{pool.idTokenA, pool.idTokenB}); - if ((!poolPairByID && poolIdByTokens) || (poolPairByID && !poolIdByTokens)) { - return Res::Err("Error, there is already a poolpair with same tokens, but different poolId"); - } + auto mismatch = (!poolPairByID && poolIdByTokens) || (poolPairByID && !poolIdByTokens); + Require(!mismatch, "Error, there is already a poolpair with same tokens, but different poolId"); // create new if (!poolPairByID && !poolIdByTokens) { @@ -89,9 +85,7 @@ Res CPoolPairView::SetPoolPair(DCT_ID const &poolId, uint32_t height, const CPoo return Res::Ok(); } - if (poolId != *poolIdByTokens) { - return Res::Err("Error, PoolID is incorrect"); - } + Require(poolId == *poolIdByTokens, "Error, PoolID is incorrect"); auto poolPairByTokens = ReadBy(poolId); assert(poolPairByTokens); @@ -122,9 +116,7 @@ Res CPoolPairView::UpdatePoolPair(DCT_ID const &poolId, const CScript &ownerAddress, const CBalances &rewards) { auto poolPair = GetPoolPair(poolId); - if (!poolPair) { - return Res::Err("Pool with poolId %s does not exist", poolId.ToString()); - } + Require(poolPair, "Pool with poolId %s does not exist", poolId.ToString()); CPoolPair &pool = poolPair.value(); @@ -133,9 +125,7 @@ Res CPoolPairView::UpdatePoolPair(DCT_ID const &poolId, } if (commission >= 0) { // default/not set is -1 - if (commission > COIN) { - return Res::Err("commission > 100%%"); - } + Require(commission <= COIN, "commission > 100%%"); pool.commission = commission; } @@ -350,18 +340,14 @@ Res CPoolPair::AddLiquidity(CAmount amountA, std::function onMint, bool slippageProtection) { // instead of assertion due to tests - if (amountA <= 0 || amountB <= 0) { - return Res::Err("amounts should be positive"); - } + Require(amountA > 0 && amountB > 0, "amounts should be positive"); CAmount liquidity{0}; if (totalLiquidity == 0) { - liquidity = (CAmount)(arith_uint256(amountA) * arith_uint256(amountB)) + liquidity = (arith_uint256(amountA) * amountB) .sqrt() .GetLow64(); // sure this is below std::numeric_limits::max() due to sqrt natue - if (liquidity <= MINIMUM_LIQUIDITY) { // ensure that it'll be non-zero - return Res::Err("liquidity too low"); - } + Require(liquidity > MINIMUM_LIQUIDITY, "liquidity too low"); liquidity -= MINIMUM_LIQUIDITY; // MINIMUM_LIQUIDITY is a hack for non-zero division totalLiquidity = MINIMUM_LIQUIDITY; @@ -370,33 +356,26 @@ Res CPoolPair::AddLiquidity(CAmount amountA, CAmount liqB = (arith_uint256(amountB) * arith_uint256(totalLiquidity) / reserveB).GetLow64(); liquidity = std::min(liqA, liqB); - if (liquidity == 0) { - return Res::Err("amounts too low, zero liquidity"); - } + Require(liquidity > 0, "amounts too low, zero liquidity"); if (slippageProtection) { - if ((std::max(liqA, liqB) - liquidity) * 100 / liquidity >= 3) { - return Res::Err("Exceeds max ratio slippage protection of 3%%"); - } + Require((std::max(liqA, liqB) - liquidity) * 100 / liquidity < 3, + "Exceeds max ratio slippage protection of 3%%"); } } // increasing totalLiquidity auto resTotal = SafeAdd(totalLiquidity, liquidity); - if (!resTotal.ok) { - return Res::Err("can't add %d to totalLiquidity: %s", liquidity, resTotal.msg); - } - totalLiquidity = *resTotal.val; + Require(resTotal, "can't add %d to totalLiquidity: %s", liquidity, resTotal.msg); + totalLiquidity = resTotal; // increasing reserves auto resA = SafeAdd(reserveA, amountA); auto resB = SafeAdd(reserveB, amountB); - if (resA.ok && resB.ok) { - reserveA = *resA.val; - reserveB = *resB.val; - } else { - return Res::Err("overflow when adding to reserves"); - } + Require(resA && resB, "overflow when adding to reserves"); + + reserveA = resA; + reserveB = resB; return onMint(liquidity); } @@ -405,9 +384,7 @@ Res CPoolPair::RemoveLiquidity(CAmount liqAmount, std::function= totalLiquidity) { - return Res::Err("incorrect liquidity"); - } + Require(liqAmount > 0 && liqAmount < totalLiquidity, "incorrect liquidity"); CAmount resAmountA, resAmountB; resAmountA = (arith_uint256(liqAmount) * arith_uint256(reserveA) / totalLiquidity).GetLow64(); @@ -426,12 +403,11 @@ Res CPoolPair::Swap(CTokenAmount in, const std::pair &asymmetricFee, std::function onTransfer, int height) { - if (in.nTokenId != idTokenA && in.nTokenId != idTokenB) - return Res::Err("Error, input token ID (" + in.nTokenId.ToString() + ") doesn't match pool tokens (" + - idTokenA.ToString() + "," + idTokenB.ToString() + ")"); + Require(in.nTokenId == idTokenA || in.nTokenId == idTokenB, + "Error, input token ID (" + in.nTokenId.ToString() + ") doesn't match pool tokens (" + idTokenA.ToString() + + "," + idTokenB.ToString() + ")"); - if (!status) - return Res::Err("Pool trading is turned off!"); + Require(status, "Pool trading is turned off!"); const bool forward = in.nTokenId == idTokenA; auto &reserveF = forward ? reserveA : reserveB; @@ -439,16 +415,14 @@ Res CPoolPair::Swap(CTokenAmount in, // it is important that reserves are at least SLOPE_SWAP_RATE (1000) to be able to slide, otherwise it can lead to // underflow - if (reserveA < SLOPE_SWAP_RATE || reserveB < SLOPE_SWAP_RATE) - return Res::Err("Lack of liquidity."); + Require(reserveA >= SLOPE_SWAP_RATE && reserveB >= SLOPE_SWAP_RATE, "Lack of liquidity."); const auto maxPrice256 = arith_uint256(maxPrice.integer) * PRECISION + maxPrice.fraction; // NOTE it has a bug prior Dakota hardfork const auto price = height < Params().GetConsensus().DakotaHeight ? arith_uint256(reserveT) * PRECISION / reserveF : arith_uint256(reserveF) * PRECISION / reserveT; - if (price > maxPrice256) - return Res::Err("Price is higher than indicated."); + Require(price <= maxPrice256, "Price is higher than indicated."); // claim trading fee if (commission) { const CAmount tradeFee = MultiplyAmounts(in.nValue, commission); @@ -461,18 +435,14 @@ Res CPoolPair::Swap(CTokenAmount in, } CTokenAmount dexfeeInAmount{in.nTokenId, 0}; + if (dexfeeInPct > 0 && poolInFee(forward, asymmetricFee)) { - if (dexfeeInPct > COIN) { - return Res::Err("Dex fee input percentage over 100%%"); - } + Require(dexfeeInPct <= COIN, "Dex fee input percentage over 100%%"); dexfeeInAmount.nValue = MultiplyAmounts(in.nValue, dexfeeInPct); in.nValue -= dexfeeInAmount.nValue; } - auto checkRes = SafeAdd(reserveF, in.nValue); - if (!checkRes) { - return Res::Err("Swapping will lead to pool's reserve overflow"); - } + Require(SafeAdd(reserveF, in.nValue), "Swapping will lead to pool's reserve overflow"); CAmount result = slopeSwap(in.nValue, reserveF, reserveT, height); @@ -681,9 +651,7 @@ inline CAmount PoolRewardPerBlock(CAmount dailyReward, CAmount rewardPct) { } Res CPoolPairView::SetRewardPct(DCT_ID const &poolId, uint32_t height, CAmount rewardPct) { - if (!HasPoolPair(poolId)) { - return Res::Err("No such pool pair"); - } + Require(HasPoolPair(poolId), "No such pool pair"); WriteBy(poolId, rewardPct); if (auto dailyReward = ReadBy(DCT_ID{})) { WriteBy(PoolHeightKey{poolId, height}, PoolRewardPerBlock(*dailyReward, rewardPct)); @@ -692,9 +660,7 @@ Res CPoolPairView::SetRewardPct(DCT_ID const &poolId, uint32_t height, CAmount r } Res CPoolPairView::SetRewardLoanPct(DCT_ID const &poolId, uint32_t height, CAmount rewardLoanPct) { - if (!HasPoolPair(poolId)) { - return Res::Err("No such pool pair"); - } + Require(HasPoolPair(poolId), "No such pool pair"); WriteBy(poolId, rewardLoanPct); if (auto dailyReward = ReadBy(DCT_ID{})) { WriteBy(PoolHeightKey{poolId, height}, PoolRewardPerBlock(*dailyReward, rewardLoanPct)); @@ -748,9 +714,7 @@ void CPoolPairView::ForEachPoolShare(std::function COIN) { - return Res::Err("Token dex fee should be in percentage"); - } + Require(feePct >= 0 && feePct <= COIN, "Token dex fee should be in percentage"); WriteBy(std::make_pair(poolId, tokenId), uint32_t(feePct)); return Res::Ok(); } diff --git a/src/masternodes/res.h b/src/masternodes/res.h index bd75f28045a..c8b4a3b7750 100644 --- a/src/masternodes/res.h +++ b/src/masternodes/res.h @@ -1,9 +1,10 @@ #ifndef DEFI_MASTERNODES_RES_H #define DEFI_MASTERNODES_RES_H -#include +#include #include #include +#include struct Res { bool ok; @@ -36,6 +37,16 @@ struct Res { return Res{true, tfm::format(msg, args...), 0, {}}; } + template + static Res Err(const T &t, std::index_sequence) { + using arg0 = std::tuple_element_t<0, std::decay_t>; + if constexpr (std::is_convertible_v) { + return Res::ErrCode(std::get(t)...); + } else { + return Res::Err(std::get(t)...); + } + } + static Res Ok() { return Res{true, {}, 0, {}}; } }; @@ -67,6 +78,11 @@ struct ResVal : public Res { return *val; } + const T *operator->() const { + assert(ok); + return &(*val); + } + T &operator*() { assert(ok); return *val; @@ -88,4 +104,28 @@ struct ResVal : public Res { } }; +template +Res CheckRes(T &&res, std::tuple &&args) { + if (res) { + return Res::Ok(); + } + constexpr auto size = sizeof...(Args); + if constexpr (size == 0) { + static_assert(std::is_convertible_v); + return std::forward(res); + } else if constexpr (std:: + is_invocable_r_v>, std::string>) { + static_assert(std::is_convertible_v); + return Res::Err(std::invoke(std::get<0>(args), res.msg)); + } else { + return Res::Err(args, std::make_index_sequence{}); + } +} + +#define Require(x, ...) \ + do { \ + if (auto __res = ::CheckRes(x, std::make_tuple(__VA_ARGS__)); !__res) \ + return __res; \ + } while (0) + #endif // DEFI_MASTERNODES_RES_H diff --git a/src/masternodes/rpc_oracles.cpp b/src/masternodes/rpc_oracles.cpp index 0f49a4858e5..95827037ac0 100644 --- a/src/masternodes/rpc_oracles.cpp +++ b/src/masternodes/rpc_oracles.cpp @@ -47,7 +47,7 @@ namespace { currency = trim_ws(currency).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); if (token.empty() || currency.empty()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, Res::Err("%s/%s is empty", oraclefields::Token, oraclefields::Currency).msg); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s/%s is empty", oraclefields::Token, oraclefields::Currency)); } return std::make_pair(token, currency); @@ -853,14 +853,8 @@ ResVal GetAggregatePrice(CCustomCSView& view, const std::string& token, }); static const uint64_t minimumLiveOracles = Params().NetworkIDString() == CBaseChainParams::REGTEST ? 1 : 2; - - if (numLiveOracles < minimumLiveOracles) { - return Res::Err("no live oracles for specified request"); - } - - if (sumWeights == 0) { - return Res::Err("all live oracles which meet specified request, have zero weight"); - } + Require(numLiveOracles >= minimumLiveOracles, "no live oracles for specified request"); + Require(sumWeights > 0, "all live oracles which meet specified request, have zero weight"); ResVal res((weightedSum / arith_uint256(sumWeights)).GetLow64(), Res::Ok()); diff --git a/src/masternodes/rpc_poolpair.cpp b/src/masternodes/rpc_poolpair.cpp index d112ea4de0f..83453c640d4 100644 --- a/src/masternodes/rpc_poolpair.cpp +++ b/src/masternodes/rpc_poolpair.cpp @@ -691,6 +691,7 @@ UniValue createpoolpair(const JSONRPCRequest &request) { // Set change to selected foundation address CTxDestination dest; + ExtractDestination(*auths.cbegin(), dest); if (IsValidDestination(dest)) { coinControl.destChange = dest; diff --git a/src/masternodes/tokens.cpp b/src/masternodes/tokens.cpp index e8e4dc1dbb9..e70a54bd0a2 100644 --- a/src/masternodes/tokens.cpp +++ b/src/masternodes/tokens.cpp @@ -23,19 +23,18 @@ std::optional CTokensView::GetToken(DCT_ID id) const { std::optional>> CTokensView::GetToken( const std::string &symbolKey) const { DCT_ID id; - if (ReadBy(symbolKey, id)) { + if (ReadBy(symbolKey, id)) return std::make_pair(id, GetToken(id)); - } + return {}; } std::optional> CTokensView::GetTokenByCreationTx(const uint256 &txid) const { DCT_ID id; - if (ReadBy(txid, id)) { - if (auto tokenImpl = ReadBy(id)) { + if (ReadBy(txid, id)) + if (auto tokenImpl = ReadBy(id)) return std::make_pair(id, std::move(*tokenImpl)); - } - } + return {}; } @@ -64,20 +63,16 @@ Res CTokensView::CreateDFIToken() { ResVal CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bool isPreBayfront) { // this should not happen, but for sure - if (GetTokenByCreationTx(token.creationTx)) { - return Res::Err("token with creation tx %s already exists!", token.creationTx.ToString()); - } + Require(!GetTokenByCreationTx(token.creationTx), + "token with creation tx %s already exists!", + token.creationTx.ToString()); - auto checkSymbolRes = token.IsValidSymbol(); - if (!checkSymbolRes.ok) { - return checkSymbolRes; - } + Require(token.IsValidSymbol()); DCT_ID id{0}; if (token.IsDAT()) { - if (GetToken(token.symbol)) { - return Res::Err("token '%s' already exists!", token.symbol); - } + Require(!GetToken(token.symbol), "token '%s' already exists!", token.symbol); + ForEachToken( [&](DCT_ID const ¤tId, CLazySerialize) { if (currentId < DCT_ID_START) @@ -86,15 +81,15 @@ ResVal CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bo }, id); if (id == DCT_ID_START) { - if (isPreBayfront) - return Res::Err( - "Critical fault: trying to create DCT_ID same as DCT_ID_START for Foundation owner\n"); // asserted - // before - // BayfrontHeight, - // keep it - // for - // strict - // sameness + Require( + !isPreBayfront, + "Critical fault: trying to create DCT_ID same as DCT_ID_START for Foundation owner\n"); // asserted + // before + // BayfrontHeight, + // keep it for + // strict + // sameness + id = IncrementLastDctId(); LogPrintf("Warning! Range CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bo Res CTokensView::UpdateToken(const CTokenImpl &newToken, bool isPreBayfront, const bool tokenSplitUpdate) { auto pair = GetTokenByCreationTx(newToken.creationTx); - if (!pair) { - return Res::Err("token with creationTx %s does not exist!", newToken.creationTx.ToString()); - } + Require(pair, "token with creationTx %s does not exist!", newToken.creationTx.ToString()); + DCT_ID id = pair->first; CTokenImpl &oldToken = pair->second; - if (!isPreBayfront && - oldToken.IsFinalized()) { // for compatibility, in potential case when someone cheat and create finalized token - // with old node (and then alter dat for ex.) - return Res::Err("can't alter 'Finalized' tokens"); - } + if (!isPreBayfront) + // for compatibility, in potential case when someone cheat and create finalized token with old node (and then + // alter dat for ex.) + Require(!oldToken.IsFinalized(), "can't alter 'Finalized' tokens"); // 'name' and 'symbol' were trimmed in 'Apply' oldToken.name = newToken.name; // check new symbol correctness if (!tokenSplitUpdate) { - auto checkSymbolRes = newToken.IsValidSymbol(); - if (!checkSymbolRes.ok) { - return checkSymbolRes; - } + Require(newToken.IsValidSymbol()); } // deal with DB symbol indexes before touching symbols/DATs: @@ -142,9 +132,8 @@ Res CTokensView::UpdateToken(const CTokenImpl &newToken, bool isPreBayfront, con // create keys with regard of new flag std::string oldSymbolKey = oldToken.CreateSymbolKey(id); std::string newSymbolKey = newToken.CreateSymbolKey(id); - if (GetToken(newSymbolKey)) { - return Res::Err("token with key '%s' already exists!", newSymbolKey); - } + Require(!GetToken(newSymbolKey), "token with key '%s' already exists!", newSymbolKey); + EraseBy(oldSymbolKey); WriteBy(newSymbolKey, id); } @@ -197,9 +186,9 @@ Res CTokensView::BayfrontFlagsCleanup() { LogPrintf("Warning! Got `LPS` token, id=%s\n", id.ToString().c_str()); changed = true; } - if (changed) { + if (changed) WriteBy(id, token); - } + return true; }, DCT_ID{1}); // start from non-DFI @@ -208,14 +197,11 @@ Res CTokensView::BayfrontFlagsCleanup() { Res CTokensView::AddMintedTokens(DCT_ID const &id, const CAmount &amount) { auto tokenImpl = GetToken(id); - if (!tokenImpl) { - return Res::Err("token with id %d does not exist!", id.v); - } + Require(tokenImpl, "token with id %d does not exist!", id.v); auto resMinted = SafeAdd(tokenImpl->minted, amount); - if (!resMinted) { - return Res::Err("overflow when adding to minted"); - } + Require(resMinted, "overflow when adding to minted"); + tokenImpl->minted = resMinted; WriteBy(id, *tokenImpl); @@ -224,14 +210,11 @@ Res CTokensView::AddMintedTokens(DCT_ID const &id, const CAmount &amount) { Res CTokensView::SubMintedTokens(DCT_ID const &id, const CAmount &amount) { auto tokenImpl = GetToken(id); - if (!tokenImpl) { - return Res::Err("token with id %d does not exist!", id.v); - } + Require(tokenImpl, "token with id %d does not exist!", id.v); auto resMinted = tokenImpl->minted - amount; - if (resMinted < 0) { - return Res::Err("not enough tokens exist to subtract this amount"); - } + Require(resMinted >= 0, "not enough tokens exist to subtract this amount"); + tokenImpl->minted = resMinted; WriteBy(id, *tokenImpl); @@ -241,9 +224,8 @@ Res CTokensView::SubMintedTokens(DCT_ID const &id, const CAmount &amount) { DCT_ID CTokensView::IncrementLastDctId() { DCT_ID result{DCT_ID_START}; auto lastDctId = ReadLastDctId(); - if (lastDctId) { + if (lastDctId) result = DCT_ID{std::max(lastDctId->v + 1, result.v)}; - } assert(Write(LastDctId::prefix(), result)); return result; } @@ -253,18 +235,15 @@ std::optional CTokensView::ReadLastDctId() const { if (Read(LastDctId::prefix(), lastDctId)) { return {lastDctId}; } + return {}; } inline Res CTokenImplementation::IsValidSymbol() const { - if (symbol.size() == 0 || IsDigit(symbol[0])) { - return Res::Err("token symbol should be non-empty and starts with a letter"); - } - if (symbol.find('#') != std::string::npos) { - return Res::Err("token symbol should not contain '#'"); - } - if (creationHeight >= Params().GetConsensus().FortCanningCrunchHeight && symbol.find('/') != std::string::npos) { - return Res::Err("token symbol should not contain '/'"); + Require(!symbol.empty() && !IsDigit(symbol[0]), "token symbol should be non-empty and starts with a letter"); + Require(symbol.find('#') == std::string::npos, "token symbol should not contain '#'"); + if (creationHeight >= Params().GetConsensus().FortCanningCrunchHeight) { + Require(symbol.find('/') == std::string::npos, "token symbol should not contain '/'"); } return Res::Ok(); } diff --git a/src/masternodes/tokens.h b/src/masternodes/tokens.h index 819d893c866..5bbc544f340 100644 --- a/src/masternodes/tokens.h +++ b/src/masternodes/tokens.h @@ -5,9 +5,8 @@ #ifndef DEFI_MASTERNODES_TOKENS_H #define DEFI_MASTERNODES_TOKENS_H -#include - #include +#include #include #include