diff --git a/src/amount.h b/src/amount.h index c94f6edaff..d00fd5a381 100644 --- a/src/amount.h +++ b/src/amount.h @@ -92,16 +92,18 @@ typedef std::map TAmounts; inline ResVal SafeAdd(CAmount _a, CAmount _b) { // check limits - Require(_a >= 0 && _b >= 0, "negative amount"); - + if (_a < 0 || _b < 0) { + return Res::Err("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 - - Require((sum - a) == b && (uint64_t)std::numeric_limits::max() >= sum, "overflow"); + if ((sum - a) != b || ((uint64_t)std::numeric_limits::max()) < sum) { + return Res::Err("overflow"); + } return {(CAmount) sum, Res::Ok()}; } @@ -125,26 +127,29 @@ struct CTokenAmount { // simple std::pair is less informative Res Add(CAmount amount) { // safety checks - Require(amount >= 0, "negative amount: %s", GetDecimaleString(amount)); - + if (amount < 0) { + return Res::Err("negative amount: %s", GetDecimaleString(amount)); + } // add - auto sumRes = SafeAdd(nValue, amount); - Require(sumRes); - - nValue = *sumRes; + auto sumRes = SafeAdd(this->nValue, amount); + if (!sumRes.ok) { + return std::move(sumRes); + } + this->nValue = *sumRes.val; return Res::Ok(); } - Res Sub(CAmount amount) { // safety checks - Require(amount >= 0, "negative amount: %s", GetDecimaleString(amount)); - Require(nValue >= amount, "amount %s is less than %s", GetDecimaleString(nValue), GetDecimaleString(amount)); - + 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)); + } // sub - nValue -= amount; + this->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 bcb7c3a977..3ea4c7c279 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1160,7 +1160,9 @@ void ClearCheckpoints(CChainParams ¶ms) { Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) { std::ifstream file(fileName); - Require(file.good(), "Could not read %s. Ensure it exists and has read permissions", fileName); + if (!file.good()) { + return Res::Err("Could not read %s. Ensure it exists and has read permissions", fileName); + } ClearCheckpoints(params); @@ -1172,13 +1174,19 @@ Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) std::istringstream iss(trimmed); std::string hashStr, heightStr; - Require((iss >> heightStr >> hashStr), "Error parsing line %s", trimmed); + if (!(iss >> heightStr >> hashStr)) { + return Res::Err("Error parsing line %s", trimmed); + } uint256 hash; - Require(ParseHashStr(hashStr, hash), "Invalid hash: %s", hashStr); + if (!ParseHashStr(hashStr, hash)) { + return Res::Err("Invalid hash: %s", hashStr); + } int32_t height; - Require(ParseInt32(heightStr, &height), "Invalid height: %s", heightStr); + if (!ParseInt32(heightStr, &height)) { + return Res::Err("Invalid height: %s", heightStr); + } params.checkpointData.mapCheckpoints[height] = hash; } diff --git a/src/masternodes/accounts.cpp b/src/masternodes/accounts.cpp index df00f43a36..0d36ca9d61 100644 --- a/src/masternodes/accounts.cpp +++ b/src/masternodes/accounts.cpp @@ -36,7 +36,10 @@ Res CAccountsView::AddBalance(const CScript &owner, CTokenAmount amount) { return Res::Ok(); } auto balance = GetBalance(owner, amount.nTokenId); - Require(balance.Add(amount.nValue)); + auto res = balance.Add(amount.nValue); + if (!res.ok) { + return res; + } return SetBalance(owner, balance); } @@ -45,21 +48,30 @@ Res CAccountsView::SubBalance(const CScript &owner, CTokenAmount amount) { return Res::Ok(); } auto balance = GetBalance(owner, amount.nTokenId); - Require(balance.Sub(amount.nValue)); + auto res = balance.Sub(amount.nValue); + if (!res.ok) { + return res; + } return SetBalance(owner, balance); } Res CAccountsView::AddBalances(const CScript &owner, const CBalances &balances) { - for (const auto &kv : balances.balances) - Require(AddBalance(owner, CTokenAmount{kv.first, kv.second})); - + for (const auto &kv : balances.balances) { + auto res = AddBalance(owner, CTokenAmount{kv.first, kv.second}); + if (!res.ok) { + return res; + } + } return Res::Ok(); } Res CAccountsView::SubBalances(const CScript &owner, const CBalances &balances) { - for (const auto &kv : balances.balances) - Require(SubBalance(owner, CTokenAmount{kv.first, kv.second})); - + for (const auto &kv : balances.balances) { + auto res = SubBalance(owner, CTokenAmount{kv.first, kv.second}); + if (!res.ok) { + return res; + } + } return Res::Ok(); } @@ -80,7 +92,10 @@ uint32_t CAccountsView::GetBalancesHeight(const CScript &owner) { } Res CAccountsView::StoreFuturesUserValues(const CFuturesUserKey &key, const CFuturesUserValue &futures) { - Require(WriteBy(key, futures), "Failed to store futures"); + if (!WriteBy(key, futures)) { + return Res::Err("Failed to store futures"); + } + return Res::Ok(); } @@ -91,12 +106,18 @@ void CAccountsView::ForEachFuturesUserValues( } Res CAccountsView::EraseFuturesUserValues(const CFuturesUserKey &key) { - Require(EraseBy(key), "Failed to erase futures"); + if (!EraseBy(key)) { + return Res::Err("Failed to erase futures"); + } + return Res::Ok(); } Res CAccountsView::StoreFuturesDUSD(const CFuturesUserKey &key, const CAmount &amount) { - Require(WriteBy(key, amount), "Failed to store futures"); + if (!WriteBy(key, amount)) { + return Res::Err("Failed to store futures"); + } + return Res::Ok(); } @@ -106,6 +127,9 @@ void CAccountsView::ForEachFuturesDUSD(std::function(key), "Failed to erase futures"); + if (!EraseBy(key)) { + return Res::Err("Failed to erase futures"); + } + return Res::Ok(); } diff --git a/src/masternodes/balances.h b/src/masternodes/balances.h index d369d092d2..2182474a9b 100644 --- a/src/masternodes/balances.h +++ b/src/masternodes/balances.h @@ -18,7 +18,10 @@ struct CBalances { return Res::Ok(); } auto current = CTokenAmount{amount.nTokenId, balances[amount.nTokenId]}; - Require(current.Add(amount.nValue)); + auto res = current.Add(amount.nValue); + if (!res.ok) { + return res; + } if (current.nValue == 0) { balances.erase(amount.nTokenId); } else { @@ -26,14 +29,15 @@ struct CBalances { } return Res::Ok(); } - Res Sub(CTokenAmount amount) { if (amount.nValue == 0) { return Res::Ok(); } auto current = CTokenAmount{amount.nTokenId, balances[amount.nTokenId]}; - Require(current.Sub(amount.nValue)); - + auto res = current.Sub(amount.nValue); + if (!res.ok) { + return res; + } if (current.nValue == 0) { balances.erase(amount.nTokenId); } else { @@ -41,7 +45,6 @@ struct CBalances { } return Res::Ok(); } - CTokenAmount SubWithRemainder(CTokenAmount amount) { if (amount.nValue == 0) { return CTokenAmount{amount.nTokenId, 0}; @@ -55,14 +58,15 @@ struct CBalances { } return CTokenAmount{amount.nTokenId, remainder}; } - Res SubBalances(const TAmounts &other) { - for (const auto &[tokenId, amount] : other) - Require(Sub(CTokenAmount{tokenId, amount})); - + for (const auto &kv : other) { + auto res = Sub(CTokenAmount{kv.first, kv.second}); + if (!res.ok) { + return res; + } + } return Res::Ok(); } - CBalances SubBalancesWithRemainder(const TAmounts &other) { CBalances remainderBalances; for (const auto &kv : other) { @@ -73,11 +77,13 @@ struct CBalances { } return remainderBalances; } - Res AddBalances(const TAmounts &other) { - for (const auto &[tokenId, amount] : other) - Require(Add(CTokenAmount{tokenId, amount})); - + for (const auto &kv : other) { + auto res = Add(CTokenAmount{kv.first, kv.second}); + if (!res.ok) { + return res; + } + } return Res::Ok(); } diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index ec98d5e8e8..392a03ba0e 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -304,13 +304,17 @@ const std::map> &ATTRIBUTES::displayKeys static ResVal VerifyInt32(const std::string &str) { int32_t int32; - Require(ParseInt32(str, &int32), "Value must be an integer"); + if (!ParseInt32(str, &int32)) { + return Res::Err("Value must be an integer"); + } return {int32, Res::Ok()}; } static ResVal VerifyPositiveInt32(const std::string &str) { int32_t int32; - Require(ParseInt32(str, &int32) && int32 >= 0, "Value must be a positive integer"); + if (!ParseInt32(str, &int32) || int32 < 0) { + return Res::Err("Value must be a positive integer"); + } return {int32, Res::Ok()}; } @@ -324,19 +328,25 @@ static ResVal VerifyUInt32(const std::string &str) { static ResVal VerifyInt64(const std::string &str) { CAmount int64; - Require(ParseInt64(str, &int64) && int64 >= 0, "Value must be a positive integer"); + if (!ParseInt64(str, &int64) || int64 < 0) { + return Res::Err("Value must be a positive integer"); + } return {int64, Res::Ok()}; } static ResVal VerifyFloat(const std::string &str) { CAmount amount = 0; - Require(ParseFixedPoint(str, 8, &amount), "Amount must be a valid number"); + if (!ParseFixedPoint(str, 8, &amount)) { + return Res::Err("Amount must be a valid number"); + } return {amount, Res::Ok()}; } ResVal VerifyPositiveFloat(const std::string &str) { CAmount amount = 0; - Require(ParseFixedPoint(str, 8, &amount) && amount >= 0, "Amount must be a positive value"); + if (!ParseFixedPoint(str, 8, &amount) || amount < 0) { + return Res::Err("Amount must be a positive value"); + } return {amount, Res::Ok()}; } @@ -377,14 +387,20 @@ static ResVal VerifyBool(const std::string &str) { static ResVal VerifySplit(const std::string &str) { OracleSplits splits; const auto pairs = KeyBreaker(str); - Require(pairs.size() == 2, "Two int values expected for split in id/mutliplier"); + if (pairs.size() != 2) { + return Res::Err("Two int values expected for split in id/mutliplier"); + } const auto resId = VerifyPositiveInt32(pairs[0]); - Require(resId); - + if (!resId) { + return resId; + } const auto resMultiplier = VerifyInt32(pairs[1]); - Require(resMultiplier); - Require(*resMultiplier != 0, "Mutliplier cannot be zero"); - + if (!resMultiplier) { + return resMultiplier; + } + if (*resMultiplier == 0) { + return Res::Err("Mutliplier cannot be zero"); + } splits[*resId] = *resMultiplier; return {splits, Res::Ok()}; @@ -430,11 +446,14 @@ static ResVal VerifyMember(const UniValue &array) { static ResVal VerifyCurrencyPair(const std::string &str) { const auto value = KeyBreaker(str); - Require(value.size() == 2, "Exactly two entires expected for currency pair"); - + if (value.size() != 2) { + return Res::Err("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); - Require(!token.empty() && !currency.empty(), "Empty token / currency"); + if (token.empty() || currency.empty()) { + return Res::Err("Empty token / currency"); + } return { CTokenCurrencyPair{token, currency}, Res::Ok() @@ -446,7 +465,9 @@ static std::set dirSet{"both", "in", "out"}; static ResVal VerifyFeeDirection(const std::string &str) { auto lowerStr = ToLower(str); const auto it = dirSet.find(lowerStr); - Require(it != dirSet.end(), "Fee direction value must be both, in or out"); + if (it == dirSet.end()) { + return Res::Err("Fee direction value must be both, in or out"); + } return {CFeeDir{static_cast(std::distance(dirSet.begin(), it))}, Res::Ok()}; } @@ -601,12 +622,12 @@ ResVal GetFutureSwapContractAddress(const std::string &contract) { return {contractAddress, Res::Ok()}; } -std::string ShowError(const std::string &key, const std::map &keys) { +static Res 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 error; + return Res::Err(error); } static void TrackLiveBalance(CCustomCSView &mnview, @@ -659,45 +680,66 @@ void TrackLiveBalances(CCustomCSView &mnview, const CBalances &balances, const u Res ATTRIBUTES::ProcessVariable(const std::string &key, const std::optional &value, std::function applyVariable) { - Require(key.size() <= 128, "Identifier exceeds maximum length (128)"); + if (key.size() > 128) { + return Res::Err("Identifier exceeds maximum length (128)"); + } const auto keys = KeyBreaker(key); - Require(!keys.empty() && !keys[0].empty(), "Empty version"); + if (keys.empty() || keys[0].empty()) { + return Res::Err("Empty version"); + } - auto iver = allowedVersions().find(keys[0]); - Require(iver != allowedVersions().end(), "Unsupported version"); + const auto &iver = allowedVersions().find(keys[0]); + if (iver == allowedVersions().end()) { + return Res::Err("Unsupported version"); + } - auto version = iver->second; - Require(version == VersionTypes::v0, "Unsupported version"); + const auto &version = iver->second; + if (version != VersionTypes::v0) { + return Res::Err("Unsupported version"); + } - Require(keys.size() >= 4 && !keys[1].empty() && !keys[2].empty() && !keys[3].empty(), - "Incorrect key for . Object of ['//ID/','value'] expected"); + if (keys.size() < 4 || keys[1].empty() || keys[2].empty() || keys[3].empty()) { + return Res::Err("Incorrect key for . Object of ['//ID/','value'] expected"); + } auto itype = allowedTypes().find(keys[1]); - Require(itype != allowedTypes().end(), ::ShowError("type", allowedTypes())); + if (itype == allowedTypes().end()) { + return ::ShowError("type", allowedTypes()); + } const auto type = itype->second; uint32_t typeId{0}; if (type == AttributeTypes::Param) { auto id = allowedParamIDs().find(keys[2]); - Require(id != allowedParamIDs().end(), ::ShowError("param", allowedParamIDs())); + if (id == allowedParamIDs().end()) { + return ::ShowError("param", allowedParamIDs()); + } typeId = id->second; } else if (type == AttributeTypes::Locks) { auto id = allowedLocksIDs().find(keys[2]); - Require(id != allowedLocksIDs().end(), ::ShowError("locks", allowedLocksIDs())); + if (id == allowedLocksIDs().end()) { + return ::ShowError("locks", allowedLocksIDs()); + } typeId = id->second; } else if (type == AttributeTypes::Oracles) { auto id = allowedOracleIDs().find(keys[2]); - Require(id != allowedOracleIDs().end(), ::ShowError("oracles", allowedOracleIDs())); + if (id == allowedOracleIDs().end()) { + return ::ShowError("oracles", allowedOracleIDs()); + } typeId = id->second; } else if (type == AttributeTypes::Governance) { auto id = allowedGovernanceIDs().find(keys[2]); - Require(id != allowedGovernanceIDs().end(), ::ShowError("governance", allowedGovernanceIDs())); + if (id == allowedGovernanceIDs().end()) { + return ::ShowError("governance", allowedGovernanceIDs()); + } typeId = id->second; } else { auto id = VerifyInt32(keys[2]); - Require(id); + if (!id) { + return std::move(id); + } typeId = *id.val; } @@ -716,7 +758,9 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } } else { auto ikey = allowedKeys().find(type); - Require(ikey != allowedKeys().end(), "Unsupported type {%d}", type); + if (ikey == allowedKeys().end()) { + return Res::Err("Unsupported type {%d}", type); + } // Alias of reward_pct in Export. if (keys[3] == "fee_pct") { @@ -724,7 +768,9 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } itype = ikey->second.find(keys[3]); - Require(itype != ikey->second.end(), ::ShowError("key", ikey->second)); + if (itype == ikey->second.end()) { + return ::ShowError("key", ikey->second); + } typeKey = itype->second; @@ -747,9 +793,10 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } } } else if (typeId == ParamIDs::DFIP2206A) { - Require(typeKey == DFIPKeys::DUSDInterestBurn || typeKey == DFIPKeys::DUSDLoanBurn, - "Unsupported type for DFIP2206A {%d}", - typeKey); + if (typeKey != DFIPKeys::DUSDInterestBurn && typeKey != DFIPKeys::DUSDLoanBurn) { + return Res::Err("Unsupported type for DFIP2206A {%d}", typeKey); + } + } else if (typeId == ParamIDs::Feature) { if (typeKey != DFIPKeys::GovUnset && typeKey != DFIPKeys::GovFoundation && typeKey != DFIPKeys::MNSetRewardAddress && typeKey != DFIPKeys::MNSetOperatorAddress && @@ -783,12 +830,18 @@ Res ATTRIBUTES::ProcessVariable(const std::string &key, } if (attrV0.IsExtendedSize()) { - Require(keys.size() == 5 && !keys[4].empty(), "Exact 5 keys are required {%d}", keys.size()); + if (keys.size() != 5 || keys[4].empty()) { + return Res::Err("Exact 5 keys are required {%d}", keys.size()); + } auto id = VerifyInt32(keys[4]); - Require(id); + if (!id) { + return std::move(id); + } attrV0.keyId = *id.val; } else { - Require(keys.size() == 4, "Exact 4 keys are required {%d}", keys.size()); + if (keys.size() != 4) { + return Res::Err("Exact 4 keys are required {%d}", keys.size()); + } } if (!value) { @@ -882,17 +935,26 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei CAccountsHistoryWriter subView( mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &subWriters); - Require(subView.SubBalance(*contractAddressValue, value.source)); + auto res = subView.SubBalance(*contractAddressValue, value.source); + if (!res) { + return res; + } subView.Flush(); CHistoryWriters addWriters{historyStore, nullptr, nullptr}; CAccountsHistoryWriter addView( mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &addWriters); - Require(addView.AddBalance(key.owner, value.source)); + res = addView.AddBalance(key.owner, value.source); + if (!res) { + return res; + } addView.Flush(); - Require(balances.Sub(value.source)); + res = balances.Sub(value.source); + if (!res) { + return res; + } } SetValue(liveKey, std::move(balances)); @@ -945,7 +1007,10 @@ Res ATTRIBUTES::RefundFuturesDUSD(CCustomCSView &mnview, const uint32_t height) } addView.Flush(); - Require(balances.Sub({DCT_ID{}, amount})); + res = balances.Sub({DCT_ID{}, amount}); + if (!res) { + return res; + } } SetValue(liveKey, std::move(balances)); @@ -954,7 +1019,9 @@ Res ATTRIBUTES::RefundFuturesDUSD(CCustomCSView &mnview, const uint32_t height) } Res ATTRIBUTES::Import(const UniValue &val) { - Require(val.isObject(), "Object of values expected"); + if (!val.isObject()) { + return Res::Err("Object of values expected"); + } std::map objMap; val.getObjMap(objMap); @@ -1026,6 +1093,7 @@ Res ATTRIBUTES::Import(const UniValue &val) { members[member.first] = member.second; } SetValue(*attrV0, members); + return Res::Ok(); } else return Res::Err("Invalid member data"); @@ -1036,7 +1104,7 @@ Res ATTRIBUTES::Import(const UniValue &val) { }); if (!res) { return res; - }; + } } return Res::Ok(); } @@ -1233,73 +1301,96 @@ UniValue ATTRIBUTES::Export() const { } Res ATTRIBUTES::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHillHeight, - "Cannot be set before FortCanningHill"); + if (view.GetLastHeight() < Params().GetConsensus().FortCanningHillHeight) + return Res::Err("Cannot be set before FortCanningHill"); for (const auto &[key, value] : attributes) { const auto attrV0 = std::get_if(&key); - Require(attrV0, "Unsupported version"); + if (!attrV0) { + return Res::Err("Unsupported version"); + } switch (attrV0->type) { case AttributeTypes::Token: switch (attrV0->key) { case TokenKeys::LoanPaybackCollateral: - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningEpilogueHeight, - "Cannot be set before FortCanningEpilogue"); + if (view.GetLastHeight() < Params().GetConsensus().FortCanningEpilogueHeight) { + return Res::Err("Cannot be set before FortCanningEpilogue"); + } [[fallthrough]]; case TokenKeys::PaybackDFI: case TokenKeys::PaybackDFIFeePCT: - Require(view.GetLoanTokenByID({attrV0->typeId}), "No such loan token (%d)", attrV0->typeId); + if (!view.GetLoanTokenByID({attrV0->typeId})) { + return Res::Err("No such loan token (%d)", attrV0->typeId); + } break; case TokenKeys::LoanPayback: case TokenKeys::LoanPaybackFeePCT: - 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); + 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); + } break; case TokenKeys::DexInFeePct: case TokenKeys::DexOutFeePct: - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, - "Cannot be set before FortCanningRoad"); - Require(view.GetToken(DCT_ID{attrV0->typeId}), "No such token (%d)", attrV0->typeId); + 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); + } break; case TokenKeys::LoanCollateralFactor: if (view.GetLastHeight() < Params().GetConsensus().FortCanningEpilogueHeight) { const auto amount = std::get_if(&value); - if (amount) - Require(*amount <= COIN, "Percentage exceeds 100%%"); + if (amount && *amount > COIN) { + return Res::Err("Percentage exceeds 100%%"); + } } [[fallthrough]]; case TokenKeys::LoanMintingInterest: if (view.GetLastHeight() < Params().GetConsensus().FortCanningGreatWorldHeight) { const auto amount = std::get_if(&value); - if (amount) - Require(*amount >= 0, "Amount must be a positive value"); + if (amount && *amount < 0) { + return Res::Err("Amount must be a positive value"); + } } [[fallthrough]]; case TokenKeys::LoanCollateralEnabled: case TokenKeys::LoanMintingEnabled: { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, - "Cannot be set before FortCanningCrunch"); - Require(VerifyToken(view, attrV0->typeId), "No such token (%d)", attrV0->typeId); + 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); + } CDataStructureV0 intervalPriceKey{ AttributeTypes::Token, attrV0->typeId, TokenKeys::FixedIntervalPriceId}; - Require(!(GetValue(intervalPriceKey, CTokenCurrencyPair{}) == CTokenCurrencyPair{}), - "Fixed interval price currency pair must be set first"); + if (GetValue(intervalPriceKey, CTokenCurrencyPair{}) == CTokenCurrencyPair{}) { + return Res::Err("Fixed interval price currency pair must be set first"); + } break; } case TokenKeys::FixedIntervalPriceId: - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, - "Cannot be set before FortCanningCrunch"); - Require(VerifyToken(view, attrV0->typeId), "No such token (%d)", attrV0->typeId); + 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); + } break; case TokenKeys::DFIP2203Enabled: - 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); + 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); + } break; case TokenKeys::Ascendant: case TokenKeys::Descendant: @@ -1357,20 +1448,31 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { break; case AttributeTypes::Oracles: - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningCrunchHeight, - "Cannot be set before FortCanningCrunch"); + if (view.GetLastHeight() < Params().GetConsensus().FortCanningCrunchHeight) { + return Res::Err("Cannot be set before FortCanningCrunch"); + } if (attrV0->typeId == OracleIDs::Splits) { const auto splitMap = std::get_if(&value); - Require(splitMap, "Unsupported value"); - + if (!splitMap) { + return Res::Err("Unsupported value"); + } for (const auto &[tokenId, multipler] : *splitMap) { - Require(tokenId != 0, "Tokenised DFI cannot be split"); - Require(!view.HasPoolPair(DCT_ID{tokenId}), "Pool tokens cannot be split"); + 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"); + } const auto token = view.GetToken(DCT_ID{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); + 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); + } } } else { return Res::Err("Unsupported key"); @@ -1381,13 +1483,18 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { switch (attrV0->key) { case PoolKeys::TokenAFeePCT: case PoolKeys::TokenBFeePCT: - Require(view.GetPoolPair({attrV0->typeId}), "No such pool (%d)", attrV0->typeId); + if (!view.GetPoolPair({attrV0->typeId})) { + return Res::Err("No such pool (%d)", attrV0->typeId); + } break; case PoolKeys::TokenAFeeDir: case PoolKeys::TokenBFeeDir: - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningSpringHeight, - "Cannot be set before FortCanningSpringHeight"); - Require(view.GetPoolPair({attrV0->typeId}), "No such pool (%d)", attrV0->typeId); + 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); + } break; default: return Res::Err("Unsupported key"); @@ -1410,8 +1517,9 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const { return Res::Err("Cannot be set before FortCanningSpringHeight"); } } else if (attrV0->typeId == ParamIDs::DFIP2203) { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningRoadHeight, - "Cannot be set before FortCanningRoadHeight"); + if (view.GetLastHeight() < Params().GetConsensus().FortCanningRoadHeight) { + return Res::Err("Cannot be set before FortCanningRoadHeight"); + } } else if (attrV0->typeId != ParamIDs::DFIP2201) { return Res::Err("Unrecognised param id"); } @@ -1457,11 +1565,15 @@ 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); - Require(pool, "No such pool (%d)", poolId.v); + if (!pool) { + return Res::Err("No such pool (%d)", poolId.v); + } auto tokenId = attrV0->key == PoolKeys::TokenAFeePCT ? pool->idTokenA : pool->idTokenB; const auto valuePct = std::get_if(&attribute.second); - Require(valuePct, "Unexpected type"); + if (!valuePct) { + return Res::Err("Unexpected type"); + } if (auto res = mnview.SetDexFeePct(poolId, tokenId, *valuePct); !res) { return res; } @@ -1473,7 +1585,9 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { std::swap(tokenA, tokenB); } const auto valuePct = std::get_if(&attribute.second); - Require(valuePct, "Unexpected type"); + if (!valuePct) { + return Res::Err("Unexpected type"); + } if (auto res = mnview.SetDexFeePct(tokenA, tokenB, *valuePct); !res) { return res; } @@ -1497,20 +1611,27 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { if (aggregatePrice) { fixedIntervalPrice.priceRecord[1] = aggregatePrice; } - Require(mnview.SetFixedIntervalPrice(fixedIntervalPrice)); + const auto res = mnview.SetFixedIntervalPrice(fixedIntervalPrice); + if (!res) { + return res; + } } else { return Res::Err("Unrecognised value for FixedIntervalPriceId"); } } else if (attrV0->key == TokenKeys::DFIP2203Enabled) { const auto value = std::get_if(&attribute.second); - Require(value, "Unexpected type"); + if (!value) { + return Res::Err("Unexpected type"); + } if (*value) { continue; } const auto token = mnview.GetLoanTokenByID(DCT_ID{attrV0->typeId}); - Require(token, "No such loan token (%d)", attrV0->typeId); + if (!token) { + return Res::Err("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. @@ -1518,12 +1639,17 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { continue; } - Require(RefundFuturesContracts(mnview, height, attrV0->typeId)); + auto res = RefundFuturesContracts(mnview, height, attrV0->typeId); + if (!res) { + return res; + } } 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); - Require(tokenInterest, "Unexpected type"); + if (!tokenInterest) { + return Res::Err("Unexpected type"); + } std::set affectedVaults; mnview.ForEachLoanTokenAmount([&](const CVaultId &vaultId, const CBalances &balances) { @@ -1556,7 +1682,6 @@ 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) { @@ -1564,10 +1689,13 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } } else { const auto factor = std::get_if(&attribute.second); - 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)); + 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)); + } } } } @@ -1575,13 +1703,19 @@ 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); - Require(value, "Unexpected type"); + if (!value) { + return Res::Err("Unexpected type"); + } if (*value) { continue; } - Require(RefundFuturesContracts(mnview, height)); + Res res = RefundFuturesContracts(mnview, height); + if (!res) { + return res; + } + } 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. @@ -1590,18 +1724,26 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2203, DFIPKeys::Active}; - Require(!GetValue(activeKey, false), "Cannot set block period while DFIP2203 is active"); + if (GetValue(activeKey, false)) { + return Res::Err("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); - Require(value, "Unexpected type"); + if (!value) { + return Res::Err("Unexpected type"); + } if (*value) { continue; } - Require(RefundFuturesDUSD(mnview, height)); + Res res = RefundFuturesDUSD(mnview, height); + if (!res) { + return res; + } + } 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. @@ -1610,42 +1752,54 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) { } CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2206F, DFIPKeys::Active}; - Require(!GetValue(activeKey, false), "Cannot set block period while DFIP2206F is active"); + if (GetValue(activeKey, false)) { + return Res::Err("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); - Require(value, "Unsupported value"); + if (!value) { + return Res::Err("Unsupported value"); + } for (const auto split : tokenSplits) { if (auto it{value->find(split)}; it == value->end()) { continue; } - Require(attrV0->key > height, "Cannot be set at or below current height"); + if (attrV0->key <= height) { + return Res::Err("Cannot be set at or below current height"); + } CDataStructureV0 lockKey{AttributeTypes::Locks, ParamIDs::TokenID, split}; if (GetValue(lockKey, false)) { continue; } - Require( - mnview.GetLoanTokenByID(DCT_ID{split}).has_value(), "Auto lock. No loan token with id (%d)", split); + if (!mnview.GetLoanTokenByID(DCT_ID{split}).has_value()) { + return Res::Err("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"); - Require(var, "Failed to create Gov var for lock"); - + if (!var) { + return Res::Err("Failed to create Gov var for lock"); + } auto govVar = std::dynamic_pointer_cast(var); - Require(govVar, "Failed to cast Gov var to ATTRIBUTES"); + if (!govVar) { + return Res::Err("Failed to cast Gov var to ATTRIBUTES"); + } govVar->attributes[lockKey] = true; CGovernanceHeightMessage lock; lock.startHeight = startHeight; lock.govVar = govVar; - - Require(storeGovVars(lock, mnview)); + auto res = storeGovVars(lock, mnview); + if (!res) { + return res; + } } else { // Less than a day's worth of blocks, apply instant lock SetValue(lockKey, true); @@ -1658,17 +1812,23 @@ 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) { - Require(ProcessVariable(key, {}, [&](const CAttributeType &attribute, const CAttributeValue &) { + auto res = ProcessVariable(key, {}, [&](const CAttributeType &attribute, const CAttributeValue &) { auto attrV0 = std::get_if(&attribute); if (!attrV0) { return Res::Ok(); } - Require(attrV0->type != AttributeTypes::Live, "Live attribute cannot be deleted"); - Require(EraseKey(attribute), "Attribute {%d} not exists", attrV0->type); + 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); + } if (attrV0->type == AttributeTypes::Poolpairs) { auto poolId = DCT_ID{attrV0->typeId}; auto pool = mnview.GetPoolPair(poolId); - Require(pool, "No such pool (%d)", poolId.v); + if (!pool) { + return Res::Err("No such pool (%d)", poolId.v); + } auto tokenId = attrV0->key == PoolKeys::TokenAFeePCT ? pool->idTokenA : pool->idTokenB; return mnview.EraseDexFeePct(poolId, tokenId); @@ -1682,7 +1842,10 @@ Res ATTRIBUTES::Erase(CCustomCSView &mnview, uint32_t, const std::vector 0, "takerFeePerBTC cannot be 0 or less"); + if (takerFeePerBTC <= 0) + return Res::Err("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 ce3889d97b..3315c1d6bf 100644 --- a/src/masternodes/govvariables/loan_daily_reward.cpp +++ b/src/masternodes/govvariables/loan_daily_reward.cpp @@ -22,7 +22,9 @@ UniValue LP_DAILY_LOAN_TOKEN_REWARD::Export() const { } Res LP_DAILY_LOAN_TOKEN_REWARD::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); + if (view.GetLastHeight() < Params().GetConsensus().FortCanningHeight) + return Res::Err("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 abf78871c5..1aa4bb904f 100644 --- a/src/masternodes/govvariables/loan_liquidation_penalty.cpp +++ b/src/masternodes/govvariables/loan_liquidation_penalty.cpp @@ -22,8 +22,11 @@ UniValue LOAN_LIQUIDATION_PENALTY::Export() const { } Res LOAN_LIQUIDATION_PENALTY::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); - Require(penalty >= COIN / 100, "Penalty cannot be less than 0.01 DFI"); + 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"); return Res::Ok(); } diff --git a/src/masternodes/govvariables/loan_splits.cpp b/src/masternodes/govvariables/loan_splits.cpp index 4fb6592bce..0964316cdf 100644 --- a/src/masternodes/govvariables/loan_splits.cpp +++ b/src/masternodes/govvariables/loan_splits.cpp @@ -13,11 +13,13 @@ bool LP_LOAN_TOKEN_SPLITS::IsEmpty() const { } Res LP_LOAN_TOKEN_SPLITS::Import(const UniValue &val) { - Require(val.isObject(), "object of {poolId: rate,... } expected"); + if (!val.isObject()) + return Res::Err("object of {poolId: rate,... } expected"); for (const std::string &key : val.getKeys()) { - auto id = DCT_ID::FromString(key); - Require(id); + const auto id = DCT_ID::FromString(key); + if (!id) + return std::move(id); splits.emplace(*id.val, AmountFromValue(val[key])); } return Res::Ok(); @@ -32,20 +34,22 @@ UniValue LP_LOAN_TOKEN_SPLITS::Export() const { } Res LP_LOAN_TOKEN_SPLITS::Validate(const CCustomCSView &mnview) const { - Require(mnview.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); + if (mnview.GetLastHeight() < Params().GetConsensus().FortCanningHeight) + return Res::Err("Cannot be set before FortCanning"); CAmount total{0}; for (const auto &kv : splits) { - Require(mnview.HasPoolPair(kv.first), "pool with id=%s not found", kv.first.ToString()); + if (!mnview.HasPoolPair(kv.first)) + return Res::Err("pool with id=%s not found", kv.first.ToString()); - Require(kv.second >= 0 && kv.second <= COIN, - "wrong percentage for pool with id=%s, value = %s", - kv.first.ToString(), - std::to_string(kv.second)); + 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)); total += kv.second; } - Require(total == COIN, "total = %d vs expected %d", total, COIN); + if (total != COIN) + return Res::Err("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 955e864aef..c3bbf28c7d 100644 --- a/src/masternodes/govvariables/lp_daily_dfi_reward.cpp +++ b/src/masternodes/govvariables/lp_daily_dfi_reward.cpp @@ -22,7 +22,9 @@ UniValue LP_DAILY_DFI_REWARD::Export() const { } Res LP_DAILY_DFI_REWARD::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() < Params().GetConsensus().EunosHeight, "Cannot be set manually after Eunos hard fork"); + if (view.GetLastHeight() >= Params().GetConsensus().EunosHeight) + return Res::Err("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 1b113ecc61..7e56a9eea3 100644 --- a/src/masternodes/govvariables/lp_splits.cpp +++ b/src/masternodes/govvariables/lp_splits.cpp @@ -13,12 +13,13 @@ bool LP_SPLITS::IsEmpty() const { } Res LP_SPLITS::Import(const UniValue &val) { - Require(val.isObject(), - "object of {poolId: rate,... } expected"); /// throw here? cause "AmountFromValue" can throw! + if (!val.isObject()) + return Res::Err("object of {poolId: rate,... } expected"); /// throw here? cause "AmountFromValue" can throw! for (const std::string &key : val.getKeys()) { - auto id = DCT_ID::FromString(key); - Require(id); + const auto id = DCT_ID::FromString(key); + if (!id) + return std::move(id); splits.emplace(*id.val, AmountFromValue(val[key])); // todo: AmountFromValue } return Res::Ok(); @@ -34,17 +35,18 @@ UniValue LP_SPLITS::Export() const { Res LP_SPLITS::Validate(const CCustomCSView &mnview) const { CAmount total{0}; - for (const auto &[poolId, amount] : splits) { - Require(mnview.HasPoolPair(poolId), "pool with id=%s not found", poolId.ToString()); + for (const auto &kv : splits) { + if (!mnview.HasPoolPair(kv.first)) + return Res::Err("pool with id=%s not found", kv.first.ToString()); - Require(amount >= 0 && amount <= COIN, - "wrong percentage for pool with id=%s, value = %s", - poolId.ToString(), - std::to_string(amount)); + 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)); - total += amount; + total += kv.second; } - Require(total == COIN, "total = %d vs expected %d", total, COIN); + if (total != COIN) + return Res::Err("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 eef339da81..30b436b74e 100644 --- a/src/masternodes/govvariables/oracle_block_interval.cpp +++ b/src/masternodes/govvariables/oracle_block_interval.cpp @@ -13,7 +13,8 @@ bool ORACLE_BLOCK_INTERVAL::IsEmpty() const { } Res ORACLE_BLOCK_INTERVAL::Import(const UniValue &val) { - Require(val.isNum(), "Block interval amount is not a number"); + if (!val.isNum()) + throw JSONRPCError(RPC_TYPE_ERROR, "Block interval amount is not a number"); blockInterval = val.get_int(); return Res::Ok(); @@ -24,8 +25,11 @@ UniValue ORACLE_BLOCK_INTERVAL::Export() const { } Res ORACLE_BLOCK_INTERVAL::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); - Require(blockInterval > 0, "Block interval cannot be less than 1"); + 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"); return Res::Ok(); } diff --git a/src/masternodes/govvariables/oracle_deviation.cpp b/src/masternodes/govvariables/oracle_deviation.cpp index cf28b8a215..bf35bbc530 100644 --- a/src/masternodes/govvariables/oracle_deviation.cpp +++ b/src/masternodes/govvariables/oracle_deviation.cpp @@ -22,8 +22,11 @@ UniValue ORACLE_DEVIATION::Export() const { } Res ORACLE_DEVIATION::Validate(const CCustomCSView &view) const { - Require(view.GetLastHeight() >= Params().GetConsensus().FortCanningHeight, "Cannot be set before FortCanning"); - Require(deviation >= COIN / 100, "Deviation cannot be less than 1 percent"); + 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"); return Res::Ok(); } diff --git a/src/masternodes/gv.cpp b/src/masternodes/gv.cpp index b91af7126c..23feec956f 100644 --- a/src/masternodes/gv.cpp +++ b/src/masternodes/gv.cpp @@ -55,8 +55,11 @@ 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) - Require(WriteBy(GovVarKey{height, item->GetName()}, *item), "Cannot write to DB"); + for (auto &item : govVars) { + if (!WriteBy(GovVarKey{height, item->GetName()}, *item)) { + return Res::Err("Cannot write to DB"); + } + } return Res::Ok(); } diff --git a/src/masternodes/icxorder.cpp b/src/masternodes/icxorder.cpp index c33af2fe9a..5bad1e5bc5 100644 --- a/src/masternodes/icxorder.cpp +++ b/src/masternodes/icxorder.cpp @@ -64,17 +64,18 @@ uint8_t CICXOrderView::GetICXOrderStatus(const OrderKey &key) const { Res CICXOrderView::ICXCreateOrder(const CICXOrderImpl &order) { // this should not happen, but for sure - 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); + 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); OrderKey key(order.idToken, order.creationTx); WriteBy(order.creationTx, order); @@ -87,9 +88,8 @@ Res CICXOrderView::ICXCreateOrder(const CICXOrderImpl &order) { Res CICXOrderView::ICXUpdateOrder(const CICXOrderImpl &order) { // this should not happen, but for sure - Require(GetICXOrderByCreationTx(order.creationTx), - "order with creation tx %s doesn't exists!", - order.creationTx.GetHex()); + if (!GetICXOrderByCreationTx(order.creationTx)) + return Res::Err("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 - 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!"); + 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!"); WriteBy(makeoffer.creationTx, makeoffer); WriteBy(TxidPairKey(makeoffer.orderTx, makeoffer.creationTx), CICXMakeOffer::STATUS_OPEN); @@ -211,12 +211,14 @@ std::unique_ptr CICXOrderView::GetICXSubmi Res CICXOrderView::ICXSubmitDFCHTLC(const CICXSubmitDFCHTLCImpl &submitdfchtlc) { // this should not happen, but for sure - 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!"); + 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!"); WriteBy(submitdfchtlc.creationTx, submitdfchtlc); WriteBy(TxidPairKey(submitdfchtlc.offerTx, submitdfchtlc.creationTx), @@ -290,15 +292,18 @@ std::unique_ptr CICXOrderView::GetICXSubmi Res CICXOrderView::ICXSubmitEXTHTLC(const CICXSubmitEXTHTLCImpl &submitexthtlc) { // this should not happen, but for sure - 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!"); + 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!"); WriteBy(submitexthtlc.creationTx, submitexthtlc); WriteBy(TxidPairKey(submitexthtlc.offerTx, submitexthtlc.creationTx), @@ -367,9 +372,8 @@ Res CICXOrderView::ICXClaimDFCHTLC(const CICXClaimDFCHTLCImpl &claimdfchtlc, const uint256 &offertxid, const CICXOrderImpl &order) { // this should not happen, but for sure - Require(!GetICXClaimDFCHTLCByCreationTx(claimdfchtlc.creationTx), - "claimdfchtlc with creation tx %s already exists!", - claimdfchtlc.creationTx.GetHex()); + if (GetICXClaimDFCHTLCByCreationTx(claimdfchtlc.creationTx)) + return Res::Err("claimdfchtlc with creation tx %s already exists!", claimdfchtlc.creationTx.GetHex()); WriteBy(claimdfchtlc.creationTx, claimdfchtlc); WriteBy(TxidPairKey(offertxid, claimdfchtlc.creationTx), CICXSubmitDFCHTLC::STATUS_CLAIMED); @@ -395,9 +399,8 @@ std::unique_ptr CICXOrderView::GetICXCloseOrd Res CICXOrderView::ICXCloseOrder(const CICXCloseOrderImpl &closeorder) { // this should not happen, but for sure - Require(!GetICXCloseOrderByCreationTx(closeorder.creationTx), - "closeorder with creation tx %s already exists!", - closeorder.creationTx.GetHex()); + if (GetICXCloseOrderByCreationTx(closeorder.creationTx)) + return Res::Err("closeorder with creation tx %s already exists!", closeorder.creationTx.GetHex()); WriteBy(closeorder.creationTx, closeorder.orderTx); @@ -414,9 +417,8 @@ std::unique_ptr CICXOrderView::GetICXCloseOff Res CICXOrderView::ICXCloseOffer(const CICXCloseOfferImpl &closeoffer) { // this should not happen, but for sure - Require(!GetICXCloseOrderByCreationTx(closeoffer.creationTx), - "closeooffer with creation tx %s already exists!", - closeoffer.creationTx.GetHex()); + if (GetICXCloseOrderByCreationTx(closeoffer.creationTx)) + return Res::Err("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 99705bc4dd..463927fb59 100644 --- a/src/masternodes/incentivefunding.cpp +++ b/src/masternodes/incentivefunding.cpp @@ -15,7 +15,9 @@ CAmount CCommunityBalancesView::GetCommunityBalance(CommunityAccountType account Res CCommunityBalancesView::SetCommunityBalance(CommunityAccountType account, CAmount amount) { // deny negative values on db level! - Require(amount >= 0, "negative amount"); + if (amount < 0) { + return Res::Err("negative amount"); + } WriteBy(static_cast(account), amount); return Res::Ok(); } @@ -33,17 +35,20 @@ Res CCommunityBalancesView::AddCommunityBalance(CommunityAccountType account, CA if (amount == 0) { return Res::Ok(); } - auto sum = SafeAdd(amount, GetCommunityBalance(account)); - Require(sum); - return SetCommunityBalance(account, sum); + auto res = SafeAdd(amount, GetCommunityBalance(account)); + return !res.ok ? res : SetCommunityBalance(account, *res.val); } Res CCommunityBalancesView::SubCommunityBalance(CommunityAccountType account, CAmount amount) { if (amount == 0) { return Res::Ok(); } - Require(amount > 0, "negative amount"); + if (amount < 0) { + return Res::Err("negative amount"); + } CAmount oldBalance = GetCommunityBalance(account); - Require(oldBalance >= amount, "Amount %d is less than %d", oldBalance, amount); + if (oldBalance < amount) { + return Res::Err("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 beca57c2b2..d01d49b138 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 - 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!"); + 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!"); WriteBy(collToken.creationTx, collToken); @@ -57,7 +57,8 @@ std::optional CLoanView::GetLoanToken(const ui Res CLoanView::SetLoanToken(const CLoanSetLoanTokenImpl &loanToken, DCT_ID const &id) { // this should not happen, but for sure - Require(!GetLoanTokenByID(id), "setLoanToken with creation tx %s already exists!", loanToken.creationTx.GetHex()); + if (GetLoanTokenByID(id)) + return Res::Err("setLoanToken with creation tx %s already exists!", loanToken.creationTx.GetHex()); WriteBy(id, loanToken); WriteBy(loanToken.creationTx, id); @@ -122,8 +123,9 @@ 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 {}; } @@ -285,16 +287,19 @@ Res CLoanView::IncreaseInterest(const uint32_t height, const CAmount tokenInterest, const CAmount loanIncreased) { const auto scheme = GetLoanScheme(loanSchemeID); - Require(scheme, "No such scheme id %s", loanSchemeID); + if (!scheme) + return Res::Err("No such scheme id %s", loanSchemeID); - auto token = GetLoanTokenByID(id); - Require(token, "No such loan token id %s", id.ToString()); + const auto token = GetLoanTokenByID(id); + if (!token) + return Res::Err("No such loan token id %s", id.ToString()); CInterestRateV3 rate{}; if (auto readRate = GetInterestRate(vaultId, id, height)) rate = *readRate; - Require(height >= rate.height, "Cannot store height in the past"); + if (rate.height > height || height == 0) + return Res::Err("Cannot store height in the past"); rate.interestToHeight = TotalInterestCalculation(rate, height); rate.height = height; @@ -332,19 +337,23 @@ Res CLoanView::DecreaseInterest(const uint32_t height, const DCT_ID id, const CAmount loanDecreased, const CAmount interestDecreased) { - const auto scheme = GetLoanScheme(loanSchemeID); - Require(scheme, "No such scheme id %s", loanSchemeID); + auto scheme = GetLoanScheme(loanSchemeID); + if (!scheme) + return Res::Err("No such scheme id %s", loanSchemeID); auto token = GetLoanTokenByID(id); - Require(token, "No such loan token id %s", id.ToString()); + if (!token) + return Res::Err("No such loan token id %s", id.ToString()); CInterestRateV3 rate{}; if (auto readRate = GetInterestRate(vaultId, id, height)) rate = *readRate; - Require(height >= rate.height, "Cannot store height in the past"); + if (rate.height > height) + return Res::Err("Cannot store height in the past"); - Require(rate.height != 0, "Data mismatch height == 0"); + if (rate.height == 0) + return Res::Err("Data mismatch height == 0"); const auto interestToHeight = TotalInterestCalculation(rate, height); const auto interestDecreasedHP = ToHigherPrecision(interestDecreased, height); @@ -526,11 +535,14 @@ void CLoanView::MigrateInterestRateToV3(CVaultView &view, uint32_t height) { } Res CLoanView::AddLoanToken(const CVaultId &vaultId, CTokenAmount amount) { - Require(GetLoanTokenByID(amount.nTokenId), "No such loan token id %s", amount.nTokenId.ToString()); + if (!GetLoanTokenByID(amount.nTokenId)) + return Res::Err("No such loan token id %s", amount.nTokenId.ToString()); CBalances amounts; ReadBy(vaultId, amounts); - Require(amounts.Add(amount)); + auto res = amounts.Add(amount); + if (!res) + return res; if (!amounts.balances.empty()) WriteBy(vaultId, amounts); @@ -539,10 +551,12 @@ Res CLoanView::AddLoanToken(const CVaultId &vaultId, CTokenAmount amount) { } Res CLoanView::SubLoanToken(const CVaultId &vaultId, CTokenAmount amount) { - Require(GetLoanTokenByID(amount.nTokenId), "No such loan token id %s", amount.nTokenId.ToString()); + if (!GetLoanTokenByID(amount.nTokenId)) + return Res::Err("No such loan token id %s", amount.nTokenId.ToString()); auto amounts = GetLoanTokens(vaultId); - Require(amounts && amounts->Sub(amount), "Loan token for vault <%s> not found", vaultId.GetHex()); + if (!amounts || !amounts->Sub(amount)) + return Res::Err("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 3c5e73ac49..8a25e7b29f 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -84,7 +84,8 @@ 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; @@ -318,12 +319,17 @@ 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) { - Require(state == CMasternode::ENABLED, "node %s state is not 'ENABLED'", nodeId.ToString()); + if (state != CMasternode::ENABLED) { + return Res::Err("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()); } - Require(!GetTimelock(nodeId, node, height), "Trying to resign masternode before timelock expiration."); + const auto timelock = GetTimelock(nodeId, node, height); + if (timelock) { + return Res::Err("Trying to resign masternode before timelock expiration."); + } node.resignTx = txid; node.resignHeight = height; @@ -449,16 +455,18 @@ 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 {}; } @@ -966,15 +974,13 @@ ResVal CCustomCSView::GetAmountInCurrency(CAmount amount, bool useNextPrice, bool requireLivePrice) { auto priceResult = GetValidatedIntervalPrice(priceFeedId, useNextPrice, requireLivePrice); - Require(priceResult); + if (!priceResult) + return priceResult; auto price = *priceResult.val; auto amountInCurrency = MultiplyAmounts(price, amount); - if (price > COIN) - Require(amountInCurrency >= amount, - "Value/price too high (%s/%s)", - GetDecimaleString(amount), - GetDecimaleString(price)); + if (price > COIN && amountInCurrency < amount) + return Res::Err("Value/price too high (%s/%s)", GetDecimaleString(amount), GetDecimaleString(price)); return {amountInCurrency, Res::Ok()}; } @@ -986,11 +992,17 @@ ResVal CCustomCSView::GetLoanCollaterals(const CVaultId &vault bool useNextPrice, bool requireLivePrice) { const auto vault = GetVault(vaultId); - Require(vault && !vault->isUnderLiquidation, "Vault is under liquidation"); + if (!vault || vault->isUnderLiquidation) + return Res::Err("Vault is under liquidation"); CCollateralLoans result{}; - Require(PopulateLoansData(result, vaultId, height, blockTime, useNextPrice, requireLivePrice)); - Require(PopulateCollateralData(result, vaultId, collaterals, height, blockTime, useNextPrice, requireLivePrice)); + 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); LogPrint(BCLog::LOAN, "%s(): totalCollaterals - %lld, totalLoans - %lld, ratio - %d\n", @@ -1009,14 +1021,16 @@ ResVal CCustomCSView::GetValidatedIntervalPrice(const CTokenCurrencyPai auto currency = priceFeedId.second; auto priceFeed = GetFixedIntervalPrice(priceFeedId); - Require(priceFeed); + if (!priceFeed) + return std::move(priceFeed); - if (requireLivePrice) - Require(priceFeed->isLive(GetPriceDeviation()), "No live fixed prices for %s/%s", tokenSymbol, currency); + if (requireLivePrice && !priceFeed.val->isLive(GetPriceDeviation())) + return Res::Err("No live fixed prices for %s/%s", tokenSymbol, currency); auto priceRecordIndex = useNextPrice ? 1 : 0; auto price = priceFeed.val->priceRecord[priceRecordIndex]; - Require(price > 0, "Negative price (%s/%s)", tokenSymbol, currency); + if (price <= 0) + return Res::Err("Negative price (%s/%s)", tokenSymbol, currency); return {price, Res::Ok()}; } @@ -1033,12 +1047,15 @@ Res CCustomCSView::PopulateLoansData(CCollateralLoans &result, for (const auto &[loanTokenId, loanTokenAmount] : loanTokens->balances) { const auto token = GetLoanTokenByID(loanTokenId); - Require(token, "Loan token with id (%s) does not exist!", loanTokenId.ToString()); + if (!token) + return Res::Err("Loan token with id (%s) does not exist!", loanTokenId.ToString()); const auto rate = GetInterestRate(vaultId, loanTokenId, height); - Require(rate, "Cannot get interest rate for token (%s)!", token->symbol); + if (!rate) + return Res::Err("Cannot get interest rate for token (%s)!", token->symbol); - Require(height >= rate->height, "Trying to read loans in the past"); + if (rate->height > height) + return Res::Err("Trying to read loans in the past"); auto totalAmount = loanTokenAmount + TotalInterest(*rate, height); if (totalAmount < 0) { @@ -1052,7 +1069,8 @@ Res CCustomCSView::PopulateLoansData(CCollateralLoans &result, auto prevLoans = result.totalLoans; result.totalLoans += *amountInCurrency.val; - Require(prevLoans <= result.totalLoans, "Exceeded maximum loans"); + if (prevLoans > result.totalLoans) + return Res::Err("Exceeded maximum loans"); result.loans.push_back({loanTokenId, amountInCurrency}); } @@ -1071,18 +1089,21 @@ Res CCustomCSView::PopulateCollateralData(CCollateralLoans &result, auto tokenAmount = col.second; auto token = HasLoanCollateralToken({tokenId, height}); - Require(token, "Collateral token with id (%s) does not exist!", tokenId.ToString()); + if (!token) + return Res::Err("Collateral token with id (%s) does not exist!", tokenId.ToString()); auto amountInCurrency = GetAmountInCurrency(tokenAmount, token->fixedIntervalPriceId, useNextPrice, requireLivePrice); - Require(amountInCurrency); + if (!amountInCurrency) + return std::move(amountInCurrency); auto amountFactor = MultiplyAmounts(token->factor, *amountInCurrency.val); auto prevCollaterals = result.totalCollaterals; result.totalCollaterals += amountFactor; - Require(prevCollaterals <= result.totalCollaterals, "Exceeded maximum collateral"); + if (prevCollaterals > result.totalCollaterals) + return Res::Err("Exceeded maximum collateral"); result.collaterals.push_back({tokenId, amountInCurrency}); } diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index d97979fdfe..67bff3dd9d 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -162,7 +162,10 @@ static ResVal BurntTokens(const CTransaction &tx) { CBalances balances; for (const auto &out : tx.vout) { if (out.scriptPubKey.size() > 0 && out.scriptPubKey[0] == OP_RETURN) { - Require(balances.Add(out.TokenAmount())); + auto res = balances.Add(out.TokenAmount()); + if (!res.ok) { + return res; + } } } return {balances, Res::Ok()}; @@ -171,7 +174,10 @@ 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++) { - Require(balances.Add(tx.vout[i].TokenAmount())); + auto res = balances.Add(tx.vout[i].TokenAmount()); + if (!res.ok) { + return res; + } } return {balances, Res::Ok()}; } @@ -304,43 +310,81 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) { extern std::string ScriptToString(const CScript &script); -template -constexpr bool FalseType = false; - -template -constexpr bool IsOneOf() { - return false; -} - -template -constexpr bool IsOneOf() { - return std::is_same_v || IsOneOf(); -} - class CCustomMetadataParseVisitor { uint32_t height; const Consensus::Params &consensus; const std::vector &metadata; - Res IsHardforkEnabled(int startHeight) const { - const std::unordered_map hardforks = { - { consensus.AMKHeight, "called before AMK height" }, - { consensus.BayfrontHeight, "called before Bayfront height" }, - { consensus.BayfrontGardensHeight, "called before Bayfront Gardens height" }, - { consensus.EunosHeight, "called before Eunos height" }, - { consensus.EunosPayaHeight, "called before EunosPaya height" }, - { consensus.FortCanningHeight, "called before FortCanning height" }, - { consensus.FortCanningHillHeight, "called before FortCanningHill height" }, - { consensus.FortCanningRoadHeight, "called before FortCanningRoad height" }, - { consensus.FortCanningEpilogueHeight, "called before FortCanningEpilogue height" }, - { consensus.GrandCentralHeight, "called before GrandCentral height" }, - }; - if (startHeight && int(height) < startHeight) { - auto it = hardforks.find(startHeight); - assert(it != hardforks.end()); - return Res::Err(it->second); + Res isPostAMKFork() const { + if (static_cast(height) < consensus.AMKHeight) { + return Res::Err("called before AMK height"); } + return Res::Ok(); + } + Res isPostBayfrontFork() const { + if (static_cast(height) < consensus.BayfrontHeight) { + return Res::Err("called before Bayfront height"); + } + return Res::Ok(); + } + + Res isPostBayfrontGardensFork() const { + if (static_cast(height) < consensus.BayfrontGardensHeight) { + return Res::Err("called before Bayfront Gardens height"); + } + return Res::Ok(); + } + + Res isPostEunosFork() const { + if (static_cast(height) < consensus.EunosHeight) { + return Res::Err("called before Eunos height"); + } + return Res::Ok(); + } + + Res isPostFortCanningFork() const { + if (static_cast(height) < consensus.FortCanningHeight) { + return Res::Err("called before FortCanning height"); + } + return Res::Ok(); + } + + Res isPostFortCanningHillFork() const { + if (static_cast(height) < consensus.FortCanningHillHeight) { + return Res::Err("called before FortCanningHill height"); + } + return Res::Ok(); + } + + Res isPostFortCanningRoadFork() const { + if (static_cast(height) < consensus.FortCanningRoadHeight) { + return Res::Err("called before FortCanningRoad height"); + } + return Res::Ok(); + } + + Res isPostFortCanningEpilogueFork() const { + if (static_cast(height) < consensus.FortCanningEpilogueHeight) { + return Res::Err("called before FortCanningEpilogue height"); + } + return Res::Ok(); + } + + Res isPostGrandCentralFork() const { + if (static_cast(height) < consensus.GrandCentralHeight) { + return Res::Err("called before GrandCentral height"); + } + return Res::Ok(); + } + + template + 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()); + } return Res::Ok(); } @@ -352,110 +396,331 @@ class CCustomMetadataParseVisitor { consensus(consensus), metadata(metadata) {} - template - Res EnabledAfter() const { - if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.AMKHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.BayfrontHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.EunosHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.FortCanningHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.BayfrontGardensHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.FortCanningHillHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.FortCanningRoadHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.FortCanningEpilogueHeight); - else if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.GrandCentralHeight); - else if constexpr (IsOneOf()) - return Res::Ok(); - else - static_assert(FalseType, "Unhandled type"); + 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"); + } + return serialize(obj); } - template - Res DisabledAfter() const { - if constexpr (IsOneOf()) - return IsHardforkEnabled(consensus.BayfrontHeight) ? Res::Err("called after Bayfront height") : Res::Ok(); + Res operator()(CUpdateMasterNodeMessage &obj) const { + auto res = isPostGrandCentralFork(); + return !res ? res : serialize(obj); + } - return Res::Ok(); + Res operator()(CCreateTokenMessage &obj) const { + auto res = isPostAMKFork(); + return !res ? res : serialize(obj); } - template - Res operator()(T& obj) const { - auto res = EnabledAfter(); - if (!res) + Res operator()(CUpdateTokenPreAMKMessage &obj) const { + auto res = isPostAMKFork(); + if (!res) { return res; + } + if (isPostBayfrontFork()) { + return Res::Err("called post Bayfront height"); + } + return serialize(obj); + } - res = DisabledAfter(); - if (!res) + Res operator()(CUpdateTokenMessage &obj) const { + auto res = isPostBayfrontFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CMintTokensMessage &obj) const { + auto res = isPostAMKFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CPoolSwapMessage &obj) const { + auto res = isPostBayfrontFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLiquidityMessage &obj) const { + auto res = isPostBayfrontFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CRemoveLiquidityMessage &obj) const { + auto res = isPostBayfrontFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CUtxosToAccountMessage &obj) const { + auto res = isPostAMKFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CAccountToUtxosMessage &obj) const { + auto res = isPostAMKFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CAccountToAccountMessage &obj) const { + auto res = isPostAMKFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CAnyAccountsToAccountsMessage &obj) const { + auto res = isPostBayfrontGardensFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CSmartContractMessage &obj) const { + auto res = isPostFortCanningHillFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CFutureSwapMessage &obj) const { + auto res = isPostFortCanningRoadFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CCreatePoolPairMessage &obj) const { + auto res = isPostBayfrontFork(); + if (!res) { return res; + } CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - ss >> obj; - if (!ss.empty()) + ss >> obj.poolPair; + ss >> obj.pairSymbol; + + // Read custom pool rewards + if (static_cast(height) >= consensus.ClarkeQuayHeight && !ss.empty()) { + ss >> obj.rewards; + } + if (!ss.empty()) { + return Res::Err("deserialization failed: excess %d bytes", ss.size()); + } + return Res::Ok(); + } + + Res operator()(CUpdatePoolPairMessage &obj) const { + auto res = isPostBayfrontFork(); + if (!res) { + return res; + } + + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); + // serialize poolId as raw integer + ss >> obj.poolId.v; + ss >> obj.status; + ss >> obj.commission; + ss >> obj.ownerAddress; + + // Read custom pool rewards + if (static_cast(height) >= consensus.ClarkeQuayHeight && !ss.empty()) { + ss >> obj.rewards; + } + + if (!ss.empty()) { return Res::Err("deserialization failed: excess %d bytes", ss.size()); + } + return Res::Ok(); + } + Res operator()(CGovernanceMessage &obj) const { + auto res = isPostBayfrontFork(); + if (!res) { + return res; + } + 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); + } + ss >> *var; + obj.govs.insert(std::move(var)); + } return Res::Ok(); } + Res operator()(CGovernanceHeightMessage &obj) const { + auto res = isPostFortCanningFork(); + if (!res) { + return res; + } + 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); + } + ss >> *obj.govVar; + ss >> obj.startHeight; + return Res::Ok(); + } + + Res operator()(CAppointOracleMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CRemoveOracleAppointMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CUpdateOracleAppointMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CSetOracleDataMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXCreateOrderMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXMakeOfferMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXSubmitDFCHTLCMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXSubmitEXTHTLCMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXClaimDFCHTLCMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXCloseOrderMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CICXCloseOfferMessage &obj) const { + auto res = isPostEunosFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CPoolSwapMessageV2 &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanSetCollateralTokenMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanSetLoanTokenMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanUpdateLoanTokenMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanSchemeMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CDefaultLoanSchemeMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CDestroyLoanSchemeMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CVaultMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CCloseVaultMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CUpdateVaultMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CDepositToVaultMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CWithdrawFromVaultMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CPaybackWithCollateralMessage &obj) const { + auto res = isPostFortCanningEpilogueFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanTakeLoanMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanPaybackLoanMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CLoanPaybackLoanV2Message &obj) const { + auto res = isPostFortCanningRoadFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CAuctionBidMessage &obj) const { + auto res = isPostFortCanningFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CBurnTokensMessage &obj) const { + auto res = isPostGrandCentralFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CCreatePropMessage &obj) const { + auto res = isPostGrandCentralFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CPropVoteMessage &obj) const { + auto res = isPostGrandCentralFork(); + return !res ? res : serialize(obj); + } + + Res operator()(CGovernanceUnsetMessage &obj) const { + auto res = isPostGrandCentralFork(); + return !res ? res : serialize(obj); + } + Res operator()(CCustomTxMessageNone &) const { return Res::Ok(); } }; @@ -470,18 +735,21 @@ CCustomTxVisitor::CCustomTxVisitor(const CTransaction &tx, coins(coins), consensus(consensus) {} -Res CCustomTxVisitor::HasAuth(const CScript &auth) const { +bool 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 Res::Ok(); + if (!coin.IsSpent() && coin.out.scriptPubKey == auth) { + return true; + } } - return Res::Err("tx must have at least one input from account owner"); + return false; } Res CCustomTxVisitor::HasCollateralAuth(const uint256 &collateralTx) const { const Coin &auth = coins.AccessCoin(COutPoint(collateralTx, 1)); // always n=1 output - Require(HasAuth(auth.out.scriptPubKey), "tx must have at least one input from the owner"); + if (!HasAuth(auth.out.scriptPubKey)) { + return Res::Err("tx must have at least one input from the owner"); + } return Res::Ok(); } @@ -508,27 +776,28 @@ Res CCustomTxVisitor::HasFoundationAuth() const { } Res CCustomTxVisitor::CheckMasternodeCreationTx() const { - 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)"); - + 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)"); + } return Res::Ok(); } Res CCustomTxVisitor::CheckTokenCreationTx() const { - 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)"); - + 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)"); + } return Res::Ok(); } Res CCustomTxVisitor::CheckCustomTx() const { - 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"); + 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"); + } return Res::Ok(); } @@ -545,13 +814,18 @@ 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()) { - Require(mnview.SubBalance(from, tokenAmount)); + auto res = mnview.SubBalance(from, tokenAmount); + if (!res) + return res; } // if "to" not supplied it will only sub balance from "form" address if (!to.empty()) { - Require(mnview.AddBalance(to, tokenAmount)); + auto res = mnview.AddBalance(to, tokenAmount); + if (!res) + return res; } + return Res::Ok(); } @@ -633,8 +907,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 @@ -652,7 +926,10 @@ Res CCustomTxVisitor::EraseEmptyBalances(TAmounts &balances) const { for (auto it = balances.begin(), next_it = it; it != balances.end(); it = next_it) { ++next_it; - Require(mnview.GetToken(it->first), "reward token %d does not exist!", it->first.v); + auto token = mnview.GetToken(it->first); + if (!token) { + return Res::Err("reward token %d does not exist!", it->first.v); + } if (it->second == 0) { balances.erase(it); @@ -667,7 +944,10 @@ 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) { - Require(mnview.SetShare(balance.first, owner, height)); + auto res = mnview.SetShare(balance.first, owner, height); + if (!res) { + return res; + } } } } @@ -680,7 +960,10 @@ 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) { - Require(mnview.DelShare(kv.first, owner)); + auto res = mnview.DelShare(kv.first, owner); + if (!res) { + return res; + } } } } @@ -705,20 +988,26 @@ Res CCustomTxVisitor::SubBalanceDelShares(const CScript &owner, const CBalances Res CCustomTxVisitor::AddBalanceSetShares(const CScript &owner, const CBalances &balance) const { CalculateOwnerRewards(owner); - Require(mnview.AddBalances(owner, balance)); - return SetShares(owner, balance.balances); + auto res = mnview.AddBalances(owner, balance); + return !res ? res : SetShares(owner, balance.balances); } Res CCustomTxVisitor::AddBalancesSetShares(const CAccounts &accounts) const { for (const auto &account : accounts) { - Require(AddBalanceSetShares(account.first, account.second)); + auto res = AddBalanceSetShares(account.first, account.second); + if (!res) { + return res; + } } return Res::Ok(); } Res CCustomTxVisitor::SubBalancesDelShares(const CAccounts &accounts) const { for (const auto &account : accounts) { - Require(SubBalanceDelShares(account.first, account.second)); + auto res = SubBalanceDelShares(account.first, account.second); + if (!res) { + return res; + } } return Res::Ok(); } @@ -728,7 +1017,9 @@ 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); - Require(!token.empty() && !currency.empty(), "empty token / currency"); + if (token.empty() || currency.empty()) { + return Res::Err("empty token / currency"); + } trimmed.emplace(token, currency); } tokenCurrency = std::move(trimmed); @@ -743,15 +1034,17 @@ Res CCustomTxVisitor::IsOnChainGovernanceEnabled() const { CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; auto attributes = mnview.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) { + return Res::Err("Attributes unavailable"); + } - Require(attributes->GetValue(enabledKey, false), "Cannot create tx, on-chain governance is not enabled"); + if (!attributes->GetValue(enabledKey, false)) { + return Res::Err("Cannot create tx, on-chain governance is not enabled"); + } return Res::Ok(); } -// -- -- -- -- -- -- -- -DONE - class CCustomTxApplyVisitor : public CCustomTxVisitor { uint64_t time; uint32_t txn; @@ -770,12 +1063,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { txn(txn) {} Res operator()(const CCreateMasterNodeMessage &obj) const { - Require(CheckMasternodeCreationTx()); + auto res = CheckMasternodeCreationTx(); + if (!res) { + return res; + } - if (height >= static_cast(consensus.EunosHeight)) - Require(HasAuth(tx.vout[1].scriptPubKey), "masternode creation needs owner auth"); + 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.EunosPayaHeight)) { + if (height >= static_cast(Params().GetConsensus().EunosPayaHeight)) { switch (obj.timelock) { case CMasternode::ZEROYEAR: case CMasternode::FIVEYEAR: @@ -784,8 +1081,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { default: return Res::Err("Timelock must be set to either 0, 5 or 10 years"); } - } else - Require(obj.timelock == 0, "collateral timelock cannot be set below EunosPaya"); + } else if (obj.timelock != 0) { + return Res::Err("collateral timelock cannot be set below EunosPaya"); + } CMasternode node; CTxDestination dest; @@ -803,8 +1101,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { node.operatorAuthAddress = obj.operatorAuthAddress; // Set masternode version2 after FC for new serialisation - if (height >= static_cast(consensus.FortCanningHeight)) + if (height >= static_cast(Params().GetConsensus().FortCanningHeight)) { node.version = CMasternode::VERSION0; + } bool duplicate{}; mnview.ForEachNewCollateral([&](const uint256 &key, CLazySerialize valueKey) { @@ -829,25 +1128,27 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { return Res::ErrCode(CustomTxErrCodes::Fatal, "Masternode exist with that owner address pending"); } - Require(mnview.CreateMasternode(tx.GetHash(), node, obj.timelock)); + res = mnview.CreateMasternode(tx.GetHash(), node, obj.timelock); // Build coinage from the point of masternode creation - - 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(); + 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; } Res operator()(const CResignMasterNodeMessage &obj) const { auto node = mnview.GetMasternode(obj); - 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); + 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); } Res operator()(const CUpdateMasterNodeMessage &obj) const { @@ -860,13 +1161,20 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } auto node = mnview.GetMasternode(obj.mnId); - Require(node, "masternode %s does not exist", obj.mnId.ToString()); + if (!node) { + return Res::Err("masternode %s does not exists", obj.mnId.ToString()); + } const auto collateralTx = node->collateralTx.IsNull() ? obj.mnId : node->collateralTx; - Require(HasCollateralAuth(collateralTx)); + const auto res = HasCollateralAuth(collateralTx); + if (!res) { + return res; + } auto state = node->GetState(height, mnview); - Require(state == CMasternode::ENABLED, "Masternode %s is not in 'ENABLED' state", obj.mnId.ToString()); + if (state != CMasternode::ENABLED) { + return Res::Err("Masternode %s is not in 'ENABLED' state", obj.mnId.ToString()); + } const auto attributes = mnview.GetAttributes(); assert(attributes); @@ -1039,17 +1347,23 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CUpdateTokenMessage &obj) const { auto pair = mnview.GetTokenByCreationTx(obj.tokenTx); - Require(pair, "token with creationTx %s does not exist", obj.tokenTx.ToString()); - Require(pair->first != DCT_ID{0}, "Can't alter DFI token!"); + 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(!mnview.AreTokensLocked({pair->first.v}), "Cannot update token during lock"); + if (mnview.AreTokensLocked({pair->first.v})) { + return Res::Err("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) - Require(!token.IsPoolShare(), - "token %s is the LPS token! Can't alter pool share's tokens!", - obj.tokenTx.ToString()); + if (token.IsPoolShare()) { + return Res::Err("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 @@ -1065,23 +1379,29 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { bool isFoundersToken = !databaseMembers.empty() ? databaseMembers.count(auth.out.scriptPubKey) > 0 : consensus.foundationMembers.count(auth.out.scriptPubKey) > 0; - if (isFoundersToken) - Require(HasFoundationAuth()); - else - Require(HasCollateralAuth(token.creationTx)); + auto res = Res::Ok(); + if (isFoundersToken && !(res = HasFoundationAuth())) { + return res; + } else if (!(res = HasCollateralAuth(token.creationTx))) { + return res; + } // 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 - Require(obj.token.IsDAT() == token.IsDAT() || HasFoundationAuth(), - "can't set isDAT to true, tx not from foundation member"); + 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"); + } + } CTokenImplementation updatedToken{obj.token}; updatedToken.creationTx = token.creationTx; updatedToken.destructionTx = token.destructionTx; updatedToken.destructionHeight = token.destructionHeight; - if (static_cast(height) >= consensus.FortCanningHeight) + if (height >= static_cast(consensus.FortCanningHeight)) { updatedToken.symbol = trim_ws(updatedToken.symbol).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH); + } return mnview.UpdateToken(updatedToken); } @@ -1223,8 +1543,7 @@ 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); @@ -1263,7 +1582,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { ownerAddress = obj.from; auto attributes = mnview.GetAttributes(); - Require(attributes, "Cannot read from attributes gov variable!"); + if (!attributes) + return Res::Err("Cannot read from attributes gov variable!"); CDataStructureV0 membersKey{AttributeTypes::Consortium, tokenId.v, ConsortiumKeys::MemberValues}; const auto members = attributes->GetValue(membersKey, CConsortiumMembers{}); @@ -1314,26 +1634,34 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CCreatePoolPairMessage &obj) const { // check foundation auth - Require(HasFoundationAuth()); - Require(obj.commission >= 0 && obj.commission <= COIN, "wrong commission"); + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member"); + } + if (obj.poolPair.commission < 0 || obj.poolPair.commission > COIN) { + return Res::Err("wrong commission"); + } - if (height >= static_cast(Params().GetConsensus().FortCanningCrunchHeight)) { - Require(obj.pairSymbol.find('/') == std::string::npos, "token symbol should not contain '/'"); + if (height >= static_cast(Params().GetConsensus().FortCanningCrunchHeight) && + obj.pairSymbol.find('/') != std::string::npos) { + return Res::Err("token symbol should not contain '/'"); } /// @todo ownerAddress validity checked only in rpc. is it enough? - CPoolPair poolPair{}; - static_cast(poolPair) = obj; + CPoolPair poolPair(obj.poolPair); auto pairSymbol = obj.pairSymbol; poolPair.creationTx = tx.GetHash(); poolPair.creationHeight = height; auto &rewards = poolPair.rewards; auto tokenA = mnview.GetToken(poolPair.idTokenA); - Require(tokenA, "token %s does not exist!", poolPair.idTokenA.ToString()); + if (!tokenA) { + return Res::Err("token %s does not exist!", poolPair.idTokenA.ToString()); + } auto tokenB = mnview.GetToken(poolPair.idTokenB); - Require(tokenB, "token %s does not exist!", poolPair.idTokenB.ToString()); + if (!tokenB) { + return Res::Err("token %s does not exist!", poolPair.idTokenB.ToString()); + } const auto symbolLength = height >= static_cast(consensus.FortCanningHeight) ? CToken::MAX_TOKEN_POOLPAIR_LENGTH @@ -1354,12 +1682,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { token.creationHeight = height; auto tokenId = mnview.CreateToken(token); - Require(tokenId); + if (!tokenId) { + return std::move(tokenId); + } rewards = obj.rewards; if (!rewards.balances.empty()) { // Check tokens exist and remove empty reward amounts - Require(EraseEmptyBalances(rewards.balances)); + auto res = EraseEmptyBalances(rewards.balances); + if (!res) { + return res; + } } return mnview.SetPoolPair(tokenId, height, poolPair); @@ -1367,7 +1700,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CUpdatePoolPairMessage &obj) const { // check foundation auth - Require(HasFoundationAuth()); + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member"); + } auto rewards = obj.rewards; if (!rewards.balances.empty()) { @@ -1376,7 +1711,10 @@ 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 - Require(EraseEmptyBalances(rewards.balances)); + auto res = EraseEmptyBalances(rewards.balances); + if (!res) { + return res; + } } } return mnview.UpdatePoolPair(obj.poolId, height, obj.status, obj.commission, obj.ownerAddress, rewards); @@ -1384,57 +1722,74 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CPoolSwapMessage &obj) const { // check auth - Require(HasAuth(obj.from)); + if (!HasAuth(obj.from)) { + return Res::Err("tx must have at least one input from account owner"); + } return CPoolSwap(obj, height).ExecuteSwap(mnview, {}); } Res operator()(const CPoolSwapMessageV2 &obj) const { // check auth - Require(HasAuth(obj.swapInfo.from)); + if (!HasAuth(obj.swapInfo.from)) { + return Res::Err("tx must have at least one input from account owner"); + } return CPoolSwap(obj.swapInfo, height).ExecuteSwap(mnview, obj.poolIDs); } Res operator()(const CLiquidityMessage &obj) const { CBalances sumTx = SumAllTransfers(obj.from); - Require(sumTx.balances.size() == 2, "the pool pair requires two tokens"); + if (sumTx.balances.size() != 2) { + return Res::Err("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? - Require(amountA.second > 0 && amountB.second > 0, "amount cannot be less than or equal to zero"); + if (amountA.second <= 0 || amountB.second <= 0) { + return Res::Err("amount cannot be less than or equal to zero"); + } auto pair = mnview.GetPoolPair(amountA.first, amountB.first); - Require(pair, "there is no such pool pair"); + if (!pair) { + return Res::Err("there is no such pool pair"); + } for (const auto &kv : obj.from) { - Require(HasAuth(kv.first)); + if (!HasAuth(kv.first)) { + return Res::Err("tx must have at least one input from account owner"); + } } for (const auto &kv : obj.from) { CalculateOwnerRewards(kv.first); - Require(mnview.SubBalances(kv.first, kv.second)); + auto res = mnview.SubBalances(kv.first, kv.second); + if (!res) { + return res; + } } 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; - Require(pool.AddLiquidity( + auto res = pool.AddLiquidity( amountA.second, amountB.second, [&] /*onMint*/ (CAmount liqAmount) { CBalances balance{TAmounts{{lpTokenID, liqAmount}}}; return AddBalanceSetShares(obj.shareAddress, balance); }, - slippageProtection)); - return mnview.SetPoolPair(lpTokenID, height, pool); + slippageProtection); + + return !res ? res : mnview.SetPoolPair(lpTokenID, height, pool); } Res operator()(const CRemoveLiquidityMessage &obj) const { @@ -1442,40 +1797,53 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { auto amount = obj.amount; // checked internally too. remove here? - Require(amount.nValue > 0, "amount cannot be less than or equal to zero"); + if (amount.nValue <= 0) { + return Res::Err("amount cannot be less than or equal to zero"); + } auto pair = mnview.GetPoolPair(amount.nTokenId); - Require(pair, "there is no such pool pair"); + if (!pair) { + return Res::Err("there is no such pool pair"); + } - Require(HasAuth(from)); + if (!HasAuth(from)) { + return Res::Err("tx must have at least one input from account owner"); + } CPoolPair &pool = pair.value(); // subtract liq.balance BEFORE RemoveLiquidity call to check balance correctness - CBalances balance{TAmounts{{amount.nTokenId, amount.nValue}}}; - Require(SubBalanceDelShares(from, balance)); + { + CBalances balance{TAmounts{{amount.nTokenId, amount.nValue}}}; + auto res = SubBalanceDelShares(from, balance); + if (!res) { + return res; + } + } - Require(pool.RemoveLiquidity(amount.nValue, [&](CAmount amountA, CAmount amountB) { + auto res = 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 mnview.SetPoolPair(amount.nTokenId, height, pool); + return !res ? res : mnview.SetPoolPair(amount.nTokenId, height, pool); } Res operator()(const CUtxosToAccountMessage &obj) const { // check enough tokens are "burnt" - auto burnt = BurntTokens(tx); - Require(burnt); + const auto burnt = BurntTokens(tx); + if (!burnt) { + return burnt; + } const auto mustBeBurnt = SumAllTransfers(obj.to); - Require(*burnt.val == mustBeBurnt, - "transfer tokens mismatch burnt tokens: (%s) != (%s)", - mustBeBurnt.ToString(), - burnt.val->ToString()); + if (*burnt.val != mustBeBurnt) { + return Res::Err( + "transfer tokens mismatch burnt tokens: (%s) != (%s)", mustBeBurnt.ToString(), burnt.val->ToString()); + } // transfer return AddBalancesSetShares(obj.to); @@ -1483,21 +1851,28 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAccountToUtxosMessage &obj) const { // check auth - Require(HasAuth(obj.from)); + if (!HasAuth(obj.from)) { + return Res::Err("tx must have at least one input from account owner"); + } // check that all tokens are minted, and no excess tokens are minted auto minted = MintedTokens(tx, obj.mintingOutputsStart); - Require(minted); + if (!minted) { + return std::move(minted); + } - Require(obj.balances == *minted.val, - "amount of minted tokens in UTXOs and metadata do not match: (%s) != (%s)", - minted.val->ToString(), - obj.balances.ToString()); + 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()); + } // block for non-DFI transactions for (const auto &kv : obj.balances.balances) { const DCT_ID &tokenId = kv.first; - Require(tokenId == DCT_ID{0}, "only available for DFI transactions"); + if (tokenId != DCT_ID{0}) { + return Res::Err("only available for DFI transactions"); + } } // transfer @@ -1506,55 +1881,69 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAccountToAccountMessage &obj) const { // check auth - Require(HasAuth(obj.from)); + if (!HasAuth(obj.from)) { + return Res::Err("tx must have at least one input from account owner"); + } // transfer - Require(SubBalanceDelShares(obj.from, SumAllTransfers(obj.to))); - return AddBalancesSetShares(obj.to); + auto res = SubBalanceDelShares(obj.from, SumAllTransfers(obj.to)); + return !res ? res : AddBalancesSetShares(obj.to); } Res HandleDFIP2201Contract(const CSmartContractMessage &obj) const { const auto attributes = mnview.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) + return Res::Err("Attributes unavailable"); CDataStructureV0 activeKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::Active}; - Require(attributes->GetValue(activeKey, false), "DFIP2201 smart contract is not enabled"); + if (!attributes->GetValue(activeKey, false)) + return Res::Err("DFIP2201 smart contract is not enabled"); - Require(obj.name == SMART_CONTRACT_DFIP_2201, "DFIP2201 contract mismatch - got: " + obj.name); + if (obj.name != SMART_CONTRACT_DFIP_2201) + return Res::Err("DFIP2201 contract mismatch - got: " + obj.name); - Require(obj.accounts.size() == 1, "Only one address entry expected for " + obj.name); + if (obj.accounts.size() != 1) + return Res::Err("Only one address entry expected for " + obj.name); - Require(obj.accounts.begin()->second.balances.size() == 1, "Only one amount entry expected for " + obj.name); + if (obj.accounts.begin()->second.balances.size() != 1) + return Res::Err("Only one amount entry expected for " + obj.name); const auto &script = obj.accounts.begin()->first; - Require(HasAuth(script), "Must have at least one input from supplied address"); + if (!HasAuth(script)) + return Res::Err("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; - Require(amount > 0, "Amount out of range"); + if (amount <= 0) + return Res::Err("Amount out of range"); CDataStructureV0 minSwapKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::MinSwap}; auto minSwap = attributes->GetValue(minSwapKey, CAmount{0}); - Require(amount >= minSwap, - "Below minimum swapable amount, must be at least " + GetDecimaleString(minSwap) + " BTC"); + if (minSwap && amount < minSwap) { + return Res::Err("Below minimum swapable amount, must be at least " + GetDecimaleString(minSwap) + " BTC"); + } const auto token = mnview.GetToken(id); - Require(token, "Specified token not found"); + if (!token) + return Res::Err("Specified token not found"); - Require(token->symbol == "BTC" && token->name == "Bitcoin" && token->IsDAT(), - "Only Bitcoin can be swapped in " + obj.name); + if (token->symbol != "BTC" || token->name != "Bitcoin" || !token->IsDAT()) + return Res::Err("Only Bitcoin can be swapped in " + obj.name); - Require(mnview.SubBalance(script, {id, amount})); + auto res = mnview.SubBalance(script, {id, amount}); + if (!res) + return res; const CTokenCurrencyPair btcUsd{"BTC", "USD"}; const CTokenCurrencyPair dfiUsd{"DFI", "USD"}; bool useNextPrice{false}, requireLivePrice{true}; auto resVal = mnview.GetValidatedIntervalPrice(btcUsd, useNextPrice, requireLivePrice); - Require(resVal); + if (!resVal) + return std::move(resVal); CDataStructureV0 premiumKey{AttributeTypes::Param, ParamIDs::DFIP2201, DFIPKeys::Premium}; auto premium = attributes->GetValue(premiumKey, CAmount{2500000}); @@ -1562,23 +1951,31 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const auto &btcPrice = MultiplyAmounts(*resVal.val, premium + COIN); resVal = mnview.GetValidatedIntervalPrice(dfiUsd, useNextPrice, requireLivePrice); - Require(resVal); + if (!resVal) + return std::move(resVal); const auto totalDFI = MultiplyAmounts(DivideAmounts(btcPrice, *resVal.val), amount); - Require(mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI})); + res = mnview.SubBalance(Params().GetConsensus().smartContracts.begin()->second, {{0}, totalDFI}); + if (!res) + return res; - Require(mnview.AddBalance(script, {{0}, totalDFI})); + res = mnview.AddBalance(script, {{0}, totalDFI}); + if (!res) + return res; return Res::Ok(); } Res operator()(const CSmartContractMessage &obj) const { - Require(!obj.accounts.empty(), "Contract account parameters missing"); + if (obj.accounts.empty()) { + return Res::Err("Contract account parameters missing"); + } auto contracts = Params().GetConsensus().smartContracts; auto contract = contracts.find(obj.name); - Require(contract != contracts.end(), "Specified smart contract not found"); + if (contract == contracts.end()) + return Res::Err("Specified smart contract not found"); // Convert to switch when it's long enough. if (obj.name == SMART_CONTRACT_DFIP_2201) @@ -1588,10 +1985,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CFutureSwapMessage &obj) const { - Require(HasAuth(obj.owner), "Transaction must have at least one input from owner"); + if (!HasAuth(obj.owner)) { + return Res::Err("Transaction must have at least one input from owner"); + } const auto attributes = mnview.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) { + return Res::Err("Attributes unavailable"); + } bool dfiToDUSD = !obj.source.nTokenId.v; const auto paramID = dfiToDUSD ? ParamIDs::DFIP2206F : ParamIDs::DFIP2203; @@ -1599,57 +2000,78 @@ 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}; - - Require( - attributes->GetValue(activeKey, false) && attributes->CheckKey(blockKey) && attributes->CheckKey(rewardKey), - "%s not currently active", - dfiToDUSD ? "DFIP2206F" : "DFIP2203"); + if (!attributes->GetValue(activeKey, false) || !attributes->CheckKey(blockKey) || + !attributes->CheckKey(rewardKey)) { + return Res::Err("%s not currently active", dfiToDUSD ? "DFIP2206F" : "DFIP2203"); + } CDataStructureV0 startKey{AttributeTypes::Param, paramID, DFIPKeys::StartBlock}; - if (const auto startBlock = attributes->GetValue(startKey, CAmount{})) { - Require( - height >= startBlock, "%s not active until block %d", dfiToDUSD ? "DFIP2206F" : "DFIP2203", startBlock); + if (const auto startBlock = attributes->GetValue(startKey, CAmount{}); height < startBlock) { + return Res::Err("%s not active until block %d", dfiToDUSD ? "DFIP2206F" : "DFIP2203", startBlock); } - Require(obj.source.nValue > 0, "Source amount must be more than zero"); + if (obj.source.nValue <= 0) { + return Res::Err("Source amount must be more than zero"); + } const auto source = mnview.GetLoanTokenByID(obj.source.nTokenId); - Require(dfiToDUSD || source, "Could not get source loan token %d", obj.source.nTokenId.v); + if (!dfiToDUSD && !source) { + return Res::Err("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); - Require(enabled, "DFIP2203 currently disabled for token %d", obj.destination); + if (!enabled) { + return Res::Err("DFIP2203 currently disabled for token %d", obj.destination); + } const auto loanToken = mnview.GetLoanTokenByID({obj.destination}); - Require(loanToken, "Could not get destination loan token %d. Set valid destination.", obj.destination); + if (!loanToken) { + return Res::Err("Could not get destination loan token %d. Set valid destination.", obj.destination); + } - Require(!mnview.AreTokensLocked({obj.destination}), "Cannot create future swap for locked token"); + if (mnview.AreTokensLocked({obj.destination})) { + return Res::Err("Cannot create future swap for locked token"); + } } else { if (!dfiToDUSD) { - Require(obj.destination == 0, "Destination should not be set when source amount is dToken or DFI"); + if (obj.destination != 0) { + return Res::Err("Destination should not be set when source amount is dToken or DFI"); + } - Require(!mnview.AreTokensLocked({obj.source.nTokenId.v}), "Cannot create future swap for locked token"); + if (mnview.AreTokensLocked({obj.source.nTokenId.v})) { + return Res::Err("Cannot create future swap for locked token"); + } CDataStructureV0 tokenKey{AttributeTypes::Token, obj.source.nTokenId.v, TokenKeys::DFIP2203Enabled}; const auto enabled = attributes->GetValue(tokenKey, true); - Require(enabled, "DFIP2203 currently disabled for token %s", obj.source.nTokenId.ToString()); + if (!enabled) { + return Res::Err("DFIP2203 currently disabled for token %s", obj.source.nTokenId.ToString()); + } } else { DCT_ID id{}; const auto token = mnview.GetTokenGuessId("DUSD", id); - Require(token, "No DUSD token defined"); + if (!token) { + return Res::Err("No DUSD token defined"); + } - Require(mnview.GetFixedIntervalPrice({"DFI", "USD"}), "DFI / DUSD fixed interval price not found"); + if (!mnview.GetFixedIntervalPrice({"DFI", "USD"})) { + return Res::Err("DFI / DUSD fixed interval price not found"); + } - Require(obj.destination == id.v, - "Incorrect destination defined for DFI swap, DUSD destination expected id: %d", - id.v); + if (obj.destination != id.v) { + return Res::Err("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); - Require(contractAddressValue); + if (!contractAddressValue) { + return contractAddressValue; + } const auto economyKey = dfiToDUSD ? EconomyKeys::DFIP2206FCurrent : EconomyKeys::DFIP2203Current; CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, economyKey}; @@ -1698,28 +2120,47 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } } - Require(totalFutures.Sub(obj.source.nValue)); + auto res = totalFutures.Sub(obj.source.nValue); + if (!res) { + return res; + } if (totalFutures.nValue > 0) { Res res{}; if (!dfiToDUSD) { - Require(mnview.StoreFuturesUserValues({height, obj.owner, txn}, {totalFutures, obj.destination})); + res = mnview.StoreFuturesUserValues({height, obj.owner, txn}, {totalFutures, obj.destination}); } else { - Require(mnview.StoreFuturesDUSD({height, obj.owner, txn}, totalFutures.nValue)); + res = mnview.StoreFuturesDUSD({height, obj.owner, txn}, totalFutures.nValue); + } + if (!res) { + return res; } } - Require(TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, *contractAddressValue, obj.owner)); + res = TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, *contractAddressValue, obj.owner); + if (!res) { + return res; + } - Require(balances.Sub(obj.source)); + res = balances.Sub(obj.source); + if (!res) { + return res; + } } else { - Require(TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, obj.owner, *contractAddressValue)); + auto res = TransferTokenBalance(obj.source.nTokenId, obj.source.nValue, obj.owner, *contractAddressValue); + if (!res) { + return res; + } if (!dfiToDUSD) { - Require(mnview.StoreFuturesUserValues({height, obj.owner, txn}, {obj.source, obj.destination})); + res = mnview.StoreFuturesUserValues({height, obj.owner, txn}, {obj.source, obj.destination}); } else { - Require(mnview.StoreFuturesDUSD({height, obj.owner, txn}, obj.source.nValue)); + res = mnview.StoreFuturesDUSD({height, obj.owner, txn}, obj.source.nValue); + } + if (!res) { + return res; } + balances.Add(obj.source); } @@ -1733,33 +2174,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CAnyAccountsToAccountsMessage &obj) const { // check auth for (const auto &kv : obj.from) { - Require(HasAuth(kv.first)); + if (!HasAuth(kv.first)) { + return Res::Err("tx must have at least one input from account owner"); + } } // compare const auto sumFrom = SumAllTransfers(obj.from); const auto sumTo = SumAllTransfers(obj.to); - Require(sumFrom == sumTo, "sum of inputs (from) != sum of outputs (to)"); + if (sumFrom != sumTo) { + return Res::Err("sum of inputs (from) != sum of outputs (to)"); + } // transfer // substraction - Require(SubBalancesDelShares(obj.from)); + auto res = SubBalancesDelShares(obj.from); // addition - return AddBalancesSetShares(obj.to); + return !res ? res : AddBalancesSetShares(obj.to); } Res operator()(const CGovernanceMessage &obj) const { // check foundation auth - Require(HasFoundationAuth()); + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member"); + } for (const auto &gov : obj.govs) { - if (!gov.second) { - return Res::Err("'%s': variable does not registered", gov.first); - } - - auto var = gov.second; Res res{}; + auto var = gov; + if (var->GetName() == "ATTRIBUTES") { // Add to existing ATTRIBUTES instead of overwriting. auto govVar = mnview.GetAttributes(); @@ -1835,7 +2279,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const auto diff = height % mnview.GetIntervalBlock(); if (diff != 0) { // Store as pending change - storeGovVars({gov.first, var, height + mnview.GetIntervalBlock() - diff}, mnview); + storeGovVars({var, height + mnview.GetIntervalBlock() - diff}, mnview); continue; } } @@ -1965,13 +2409,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } COracle oracle; static_cast(oracle) = obj.newOracleAppoint; - Require(NormalizeTokenCurrencyPair(oracle.availablePairs)); - return mnview.UpdateOracle(obj.oracleId, std::move(oracle)); + auto res = NormalizeTokenCurrencyPair(oracle.availablePairs); + return !res ? res : mnview.UpdateOracle(obj.oracleId, std::move(oracle)); } Res operator()(const CRemoveOracleAppointMessage &obj) const { - Require(HasFoundationAuth()); - + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member"); + } return mnview.RemoveOracle(obj.oracleId); } @@ -2002,7 +2447,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CICXCreateOrderMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXOrderImplemetation order; static_cast(order) = obj; @@ -2010,24 +2457,29 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { order.creationTx = tx.GetHash(); order.creationHeight = height; - Require(HasAuth(order.ownerAddress), "tx must have at least one input from order owner"); + if (!HasAuth(order.ownerAddress)) + return Res::Err("tx must have at least one input from order owner"); - Require(mnview.GetToken(order.idToken), "token %s does not exist!", order.idToken.ToString()); + if (!mnview.GetToken(order.idToken)) + return Res::Err("token %s does not exist!", order.idToken.ToString()); if (order.orderType == CICXOrder::TYPE_INTERNAL) { - Require(order.receivePubkey.IsFullyValid(), "receivePubkey must be valid pubkey"); + if (!order.receivePubkey.IsFullyValid()) + return Res::Err("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); - Require(TransferTokenBalance(order.idToken, order.amountFrom, order.ownerAddress, txidAddr)); + res = TransferTokenBalance(order.idToken, order.amountFrom, order.ownerAddress, txidAddr); } - return mnview.ICXCreateOrder(order); + return !res ? res : mnview.ICXCreateOrder(order); } Res operator()(const CICXMakeOfferMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXMakeOfferImplemetation makeoffer; static_cast(makeoffer) = obj; @@ -2035,15 +2487,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { makeoffer.creationTx = tx.GetHash(); makeoffer.creationHeight = height; - Require(HasAuth(makeoffer.ownerAddress), "tx must have at least one input from order owner"); + if (!HasAuth(makeoffer.ownerAddress)) + return Res::Err("tx must have at least one input from order owner"); auto order = mnview.GetICXOrderByCreationTx(makeoffer.orderTx); - Require(order, "order with creation tx " + makeoffer.orderTx.GetHex() + " does not exists!"); + if (!order) + return Res::Err("order with creation tx " + makeoffer.orderTx.GetHex() + " does not exists!"); auto expiry = static_cast(height) < consensus.EunosPayaHeight ? CICXMakeOffer::DEFAULT_EXPIRY : CICXMakeOffer::EUNOSPAYA_DEFAULT_EXPIRY; - Require(makeoffer.expiry >= expiry, "offer expiry must be greater than %d!", expiry - 1); + if (makeoffer.expiry < expiry) + return Res::Err("offer expiry must be greater than %d!", expiry - 1); CScript txidAddr(makeoffer.creationTx.begin(), makeoffer.creationTx.end()); @@ -2051,7 +2506,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // calculating takerFee makeoffer.takerFee = CalculateTakerFee(makeoffer.amount); } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { - Require(makeoffer.receivePubkey.IsFullyValid(), "receivePubkey must be valid pubkey"); + if (!makeoffer.receivePubkey.IsFullyValid()) + return Res::Err("receivePubkey must be valid pubkey"); // calculating takerFee CAmount BTCAmount(static_cast( @@ -2061,13 +2517,16 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // locking takerFee in offer txidaddr CalculateOwnerRewards(makeoffer.ownerAddress); - Require(TransferTokenBalance(DCT_ID{0}, makeoffer.takerFee, makeoffer.ownerAddress, txidAddr)); + res = TransferTokenBalance(DCT_ID{0}, makeoffer.takerFee, makeoffer.ownerAddress, txidAddr); - return mnview.ICXMakeOffer(makeoffer); + return !res ? res : mnview.ICXMakeOffer(makeoffer); } Res operator()(const CICXSubmitDFCHTLCMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) { + return res; + } CICXSubmitDFCHTLCImplemetation submitdfchtlc; static_cast(submitdfchtlc) = obj; @@ -2076,22 +2535,27 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { submitdfchtlc.creationHeight = height; auto offer = mnview.GetICXMakeOfferByCreationTx(submitdfchtlc.offerTx); - Require(offer, "offer with creation tx %s does not exists!", submitdfchtlc.offerTx.GetHex()); + if (!offer) + return Res::Err("offer with creation tx %s does not exists!", submitdfchtlc.offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - Require(order->creationHeight + order->expiry >= height + submitdfchtlc.timeout, - "order will expire before dfc htlc expires!"); - Require(!mnview.HasICXSubmitDFCHTLCOpen(submitdfchtlc.offerTx), "dfc htlc already submitted!"); + 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!"); CScript srcAddr; if (order->orderType == CICXOrder::TYPE_INTERNAL) { // check auth - 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()); + 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()); uint32_t timeout; if (static_cast(height) < consensus.EunosPayaHeight) @@ -2099,32 +2563,43 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { else timeout = CICXSubmitDFCHTLC::EUNOSPAYA_MINIMUM_TIMEOUT; - Require(submitdfchtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); + if (submitdfchtlc.timeout < timeout) + return Res::Err("timeout must be greater than %d", timeout - 1); srcAddr = CScript(order->creationTx.begin(), order->creationTx.end()); CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - auto calcAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); - Require(calcAmount <= offer->amount, "amount must be lower or equal the offer one"); + 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"); 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) { - auto BTCAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); - takerFee = (arith_uint256(BTCAmount) * offer->takerFee / offer->amount).GetLow64(); + 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()); } } else { - auto BTCAmount = MultiplyAmounts(submitdfchtlc.amount, order->orderPrice); - takerFee = CalculateTakerFee(BTCAmount); + CAmount BTCAmount(static_cast( + (arith_uint256(submitdfchtlc.amount) * arith_uint256(order->orderPrice) / arith_uint256(COIN)) + .GetLow64())); + takerFee = CalculateTakerFee(BTCAmount); } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { CalculateOwnerRewards(offer->ownerAddress); - Require( - TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress); + if (!res) + return res; // update the offer with adjusted takerFee offer->takerFee = takerFee; @@ -2132,31 +2607,38 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // burn takerFee - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress); + if (!res) + return res; // burn makerDeposit CalculateOwnerRewards(order->ownerAddress); - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress); + if (!res) + return res; } else if (order->orderType == CICXOrder::TYPE_EXTERNAL) { // check auth - Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); + if (!HasAuth(offer->ownerAddress)) + return Res::Err("tx must have at least one input from offer owner"); srcAddr = offer->ownerAddress; CalculateOwnerRewards(offer->ownerAddress); auto exthtlc = mnview.HasICXSubmitEXTHTLCOpen(submitdfchtlc.offerTx); - Require(exthtlc, - "offer (%s) needs to have ext htlc submitted first, but no external htlc found!", - submitdfchtlc.offerTx.GetHex()); + if (!exthtlc) + return Res::Err("offer (%s) needs to have ext htlc submitted first, but no external htlc found!", + submitdfchtlc.offerTx.GetHex()); - auto calcAmount = MultiplyAmounts(exthtlc->amount, order->orderPrice); - Require(submitdfchtlc.amount == calcAmount, "amount must be equal to calculated exthtlc amount"); + 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"); - Require(submitdfchtlc.hash == exthtlc->hash, - "Invalid hash, dfc htlc hash is different than extarnal htlc hash - %s != %s", - submitdfchtlc.hash.GetHex(), - exthtlc->hash.GetHex()); + 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()); uint32_t timeout, btcBlocksInDfi; if (static_cast(height) < consensus.EunosPayaHeight) { @@ -2167,20 +2649,24 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { btcBlocksInDfi = CICXSubmitEXTHTLC::BTC_BLOCKS_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"); + 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"); } // 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()); - Require(TransferTokenBalance(order->idToken, submitdfchtlc.amount, srcAddr, htlcTxidAddr)); - return mnview.ICXSubmitDFCHTLC(submitdfchtlc); + res = TransferTokenBalance(order->idToken, submitdfchtlc.amount, srcAddr, htlcTxidAddr); + return !res ? res : mnview.ICXSubmitDFCHTLC(submitdfchtlc); } Res operator()(const CICXSubmitEXTHTLCMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXSubmitEXTHTLCImplemetation submitexthtlc; static_cast(submitexthtlc) = obj; @@ -2189,29 +2675,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { submitexthtlc.creationHeight = height; auto offer = mnview.GetICXMakeOfferByCreationTx(submitexthtlc.offerTx); - Require(offer, "order with creation tx %s does not exists!", submitexthtlc.offerTx.GetHex()); + if (!offer) + return Res::Err("order with creation tx %s does not exists!", submitexthtlc.offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); - Require(order->creationHeight + order->expiry >= - height + (submitexthtlc.timeout * CICXSubmitEXTHTLC::BTC_BLOCKS_IN_DFI_BLOCKS), - "order will expire before ext htlc expires!"); + 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(!mnview.HasICXSubmitEXTHTLCOpen(submitexthtlc.offerTx), "ext htlc already submitted!"); + if (mnview.HasICXSubmitEXTHTLCOpen(submitexthtlc.offerTx)) + return Res::Err("ext htlc already submitted!"); if (order->orderType == CICXOrder::TYPE_INTERNAL) { - Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); + if (!HasAuth(offer->ownerAddress)) + return Res::Err("tx must have at least one input from offer owner"); auto dfchtlc = mnview.HasICXSubmitDFCHTLCOpen(submitexthtlc.offerTx); - Require(dfchtlc, - "offer (%s) needs to have dfc htlc submitted first, but no dfc htlc found!", - submitexthtlc.offerTx.GetHex()); + 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"); - 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"); + if (submitexthtlc.hash != dfchtlc->hash) + return Res::Err("Invalid hash, external htlc hash is different than dfc htlc hash"); uint32_t timeout, btcBlocksInDfi; if (static_cast(height) < consensus.EunosPayaHeight) { @@ -2222,15 +2715,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { btcBlocksInDfi = CICXSubmitEXTHTLC::EUNOSPAYA_BTC_BLOCKS_IN_DFI_BLOCKS; } - 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 < timeout) + return Res::Err("timeout must be greater than %d", timeout - 1); + 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) { - 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()); + 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()); uint32_t timeout; if (static_cast(height) < consensus.EunosPayaHeight) @@ -2238,28 +2733,38 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { else timeout = CICXSubmitEXTHTLC::EUNOSPAYA_MINIMUM_TIMEOUT; - Require(submitexthtlc.timeout >= timeout, "timeout must be greater than %d", timeout - 1); + if (submitexthtlc.timeout < timeout) + return Res::Err("timeout must be greater than %d", timeout - 1); CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); - auto calcAmount = MultiplyAmounts(submitexthtlc.amount, order->orderPrice); - Require(calcAmount <= offer->amount, "amount must be lower or equal the offer one"); + 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"); 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) { - auto BTCAmount = DivideAmounts(offer->amount, order->orderPrice); - takerFee = (arith_uint256(submitexthtlc.amount) * offer->takerFee / BTCAmount).GetLow64(); + 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()); } - } else + } else { takerFee = CalculateTakerFee(submitexthtlc.amount); + } // refund the rest of locked takerFee if there is difference if (offer->takerFee - takerFee) { CalculateOwnerRewards(offer->ownerAddress); - Require( - TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee - takerFee, offerTxidAddr, offer->ownerAddress); + if (!res) + return res; // update the offer with adjusted takerFee offer->takerFee = takerFee; @@ -2267,18 +2772,22 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // burn takerFee - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, offerTxidAddr, consensus.burnAddress); + if (!res) + return res; // burn makerDeposit CalculateOwnerRewards(order->ownerAddress); - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, order->ownerAddress, consensus.burnAddress); } - return mnview.ICXSubmitEXTHTLC(submitexthtlc); + return !res ? res : mnview.ICXSubmitEXTHTLC(submitexthtlc); } Res operator()(const CICXClaimDFCHTLCMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXClaimDFCHTLCImplemetation claimdfchtlc; static_cast(claimdfchtlc) = obj; @@ -2287,73 +2796,93 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { claimdfchtlc.creationHeight = height; auto dfchtlc = mnview.GetICXSubmitDFCHTLCByCreationTx(claimdfchtlc.dfchtlcTx); - Require(dfchtlc, "dfc htlc with creation tx %s does not exists!", claimdfchtlc.dfchtlcTx.GetHex()); + if (!dfchtlc) + return Res::Err("dfc htlc with creation tx %s does not exists!", claimdfchtlc.dfchtlcTx.GetHex()); - Require(mnview.HasICXSubmitDFCHTLCOpen(dfchtlc->offerTx), "dfc htlc not found or already claimed or refunded!"); + if (!mnview.HasICXSubmitDFCHTLCOpen(dfchtlc->offerTx)) + return Res::Err("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)); - Require(dfchtlc->hash == calcHash, - "hash generated from given seed is different than in dfc htlc: %s - %s!", - calcHash.GetHex(), - dfchtlc->hash.GetHex()); + 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()); auto offer = mnview.GetICXMakeOfferByCreationTx(dfchtlc->offerTx); - Require(offer, "offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); + if (!offer) + return Res::Err("offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - Require(order, "order with creation tx %s does not exists!", offer->orderTx.GetHex()); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); auto exthtlc = mnview.HasICXSubmitEXTHTLCOpen(dfchtlc->offerTx); - if (static_cast(height) < consensus.EunosPayaHeight) - Require(exthtlc, "cannot claim, external htlc for this offer does not exists or expired!"); + if (static_cast(height) < consensus.EunosPayaHeight && !exthtlc) + return Res::Err("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) - 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)); + 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; // refund makerDeposit - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, CScript(), order->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, CScript(), order->ownerAddress); + if (!res) + return res; // makerIncentive - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee * 25 / 100, CScript(), order->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee * 25 / 100, CScript(), order->ownerAddress); + if (!res) + return res; // 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 ((Params().NetworkIDString() == CBaseChainParams::TESTNET && height >= 1250000) || Params().NetworkIDString() == CBaseChainParams::REGTEST) { - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee * 50 / 100, CScript(), order->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee * 50 / 100, CScript(), order->ownerAddress); } else { - Require(TransferTokenBalance(BTC, offer->takerFee * 50 / 100, CScript(), order->ownerAddress)); + res = 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 -= DivideAmounts(dfchtlc->amount, order->orderPrice); + order->amountToFill -= static_cast( + (arith_uint256(dfchtlc->amount) * arith_uint256(COIN) / arith_uint256(order->orderPrice)).GetLow64()); // Order fulfilled, close order. if (order->amountToFill == 0) { order->closeTx = claimdfchtlc.creationTx; order->closeHeight = height; - Require(mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_FILLED)); + res = mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_FILLED); + if (!res) + return res; } - Require(mnview.ICXClaimDFCHTLC(claimdfchtlc, offer->creationTx, *order)); + res = mnview.ICXClaimDFCHTLC(claimdfchtlc, offer->creationTx, *order); + if (!res) + return res; // Close offer - Require(mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED)); - - Require(mnview.ICXCloseDFCHTLC(*dfchtlc, CICXSubmitDFCHTLC::STATUS_CLAIMED)); + res = mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); + if (!res) + return res; + res = mnview.ICXCloseDFCHTLC(*dfchtlc, CICXSubmitDFCHTLC::STATUS_CLAIMED); + if (!res) + return res; if (static_cast(height) >= consensus.EunosPayaHeight) { if (exthtlc) @@ -2365,7 +2894,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CICXCloseOrderMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXCloseOrderImplemetation closeorder; static_cast(closeorder) = obj; @@ -2373,16 +2904,19 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { closeorder.creationTx = tx.GetHash(); closeorder.creationHeight = height; - auto order = mnview.GetICXOrderByCreationTx(closeorder.orderTx); - Require(order, "order with creation tx %s does not exists!", closeorder.orderTx.GetHex()); + 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()); - 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()); + if (!mnview.HasICXOrderOpen(order->idToken, order->creationTx)) + return Res::Err("order with creation tx %s is already closed!", closeorder.orderTx.GetHex()); // check auth - Require(HasAuth(order->ownerAddress), "tx must have at least one input from order owner"); + if (!HasAuth(order->ownerAddress)) + return Res::Err("tx must have at least one input from order owner"); order->closeTx = closeorder.creationTx; order->closeHeight = closeorder.creationHeight; @@ -2391,15 +2925,19 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // subtract the balance from txidAddr and return to owner CScript txidAddr(order->creationTx.begin(), order->creationTx.end()); CalculateOwnerRewards(order->ownerAddress); - Require(TransferTokenBalance(order->idToken, order->amountToFill, txidAddr, order->ownerAddress)); + res = TransferTokenBalance(order->idToken, order->amountToFill, txidAddr, order->ownerAddress); + if (!res) + return res; } - Require(mnview.ICXCloseOrder(closeorder)); - return mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_CLOSED); + res = mnview.ICXCloseOrder(closeorder); + return !res ? res : mnview.ICXCloseOrderTx(*order, CICXOrder::STATUS_CLOSED); } Res operator()(const CICXCloseOfferMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; CICXCloseOfferImplemetation closeoffer; static_cast(closeoffer) = obj; @@ -2407,19 +2945,23 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { closeoffer.creationTx = tx.GetHash(); closeoffer.creationHeight = height; - auto offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx); - Require(offer, "offer with creation tx %s does not exists!", closeoffer.offerTx.GetHex()); + std::unique_ptr offer; + if (!(offer = mnview.GetICXMakeOfferByCreationTx(closeoffer.offerTx))) + return Res::Err("offer with creation tx %s does not exists!", 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 (!offer->closeTx.IsNull()) + return Res::Err("offer with creation tx %s is already closed!", closeoffer.offerTx.GetHex()); - auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); - Require(order, "order with creation tx %s does not exists!", offer->orderTx.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()); // check auth - Require(HasAuth(offer->ownerAddress), "tx must have at least one input from offer owner"); + if (!HasAuth(offer->ownerAddress)) + return Res::Err("tx must have at least one input from offer owner"); offer->closeTx = closeoffer.creationTx; offer->closeHeight = closeoffer.creationHeight; @@ -2431,26 +2973,36 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // subtract takerFee from txidAddr and return to owner CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); CalculateOwnerRewards(offer->ownerAddress); - Require(TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress)); + res = TransferTokenBalance(DCT_ID{0}, offer->takerFee, txidAddr, offer->ownerAddress); + if (!res) + return res; } 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) - 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)); + 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; + } } - Require(mnview.ICXCloseOffer(closeoffer)); - return mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); + res = mnview.ICXCloseOffer(closeoffer); + return !res ? res : mnview.ICXCloseMakeOfferTx(*offer, CICXMakeOffer::STATUS_CLOSED); } Res operator()(const CLoanSetCollateralTokenMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; - Require(HasFoundationAuth(), "tx not from foundation member!"); + if (!HasFoundationAuth()) + return Res::Err("tx not from foundation member!"); if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &tokenId = obj.idToken.v; @@ -2463,18 +3015,30 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, tokenId, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - Require(gv, "Failed to create ATTRIBUTES Governance variable"); + if (!gv) { + return Res::Err("Failed to create ATTRIBUTES Governance variable"); + } auto var = std::dynamic_pointer_cast(gv); - Require(var, "Failed to convert ATTRIBUTES Governance variable"); + if (!var) { + return Res::Err("Failed to convert ATTRIBUTES Governance variable"); + } var->SetValue(collateralEnabled, true); var->SetValue(collateralFactor, obj.factor); var->SetValue(pairKey, obj.fixedIntervalPriceId); - Require(attributes->Import(var->Export())); - Require(attributes->Validate(mnview)); - Require(attributes->Apply(mnview, height)); + 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; return mnview.SetVariable(*attributes); } @@ -2486,41 +3050,48 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { collToken.creationHeight = height; auto token = mnview.GetToken(collToken.idToken); - Require(token, "token %s does not exist!", collToken.idToken.ToString()); + if (!token) + return Res::Err("token %s does not exist!", collToken.idToken.ToString()); if (!collToken.activateAfterBlock) collToken.activateAfterBlock = height; - Require(collToken.activateAfterBlock >= height, "activateAfterBlock cannot be less than current height!"); + if (collToken.activateAfterBlock < height) + return Res::Err("activateAfterBlock cannot be less than current height!"); - Require(OraclePriceFeed(mnview, collToken.fixedIntervalPriceId), - "Price feed %s/%s does not belong to any oracle", - collToken.fixedIntervalPriceId.first, - collToken.fixedIntervalPriceId.second); + if (!OraclePriceFeed(mnview, collToken.fixedIntervalPriceId)) + return Res::Err("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); - Require(price, price.msg); + if (!price) + return Res::Err(price.msg); fixedIntervalPrice.priceRecord[1] = price; fixedIntervalPrice.timestamp = time; auto resSetFixedPrice = mnview.SetFixedIntervalPrice(fixedIntervalPrice); - Require(resSetFixedPrice, resSetFixedPrice.msg); + if (!resSetFixedPrice) + return Res::Err(resSetFixedPrice.msg); return mnview.CreateLoanCollateralToken(collToken); } Res operator()(const CLoanSetLoanTokenMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; - Require(HasFoundationAuth(), "tx not from foundation member!"); + if (!HasFoundationAuth()) + return Res::Err("tx not from foundation member!"); - if (height < static_cast(consensus.FortCanningGreatWorldHeight)) { - Require(obj.interest >= 0, "interest rate cannot be less than 0!"); + if (obj.interest < 0 && height < static_cast(consensus.FortCanningGreatWorldHeight)) { + return Res::Err("interest rate cannot be less than 0!"); } CTokenImplementation token; @@ -2534,7 +3105,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { static_cast(CToken::TokenFlags::LoanToken) | static_cast(CToken::TokenFlags::DAT); auto tokenId = mnview.CreateToken(token); - Require(tokenId); + if (!tokenId) + return std::move(tokenId); if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &id = tokenId.val->v; @@ -2547,18 +3119,31 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, id, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - Require(gv, "Failed to create ATTRIBUTES Governance variable"); + if (!gv) { + return Res::Err("Failed to create ATTRIBUTES Governance variable"); + } auto var = std::dynamic_pointer_cast(gv); - Require(var, "Failed to convert ATTRIBUTES Governance variable"); + if (!var) { + return Res::Err("Failed to convert ATTRIBUTES Governance variable"); + } var->SetValue(mintEnabled, obj.mintable); var->SetValue(mintInterest, obj.interest); var->SetValue(pairKey, obj.fixedIntervalPriceId); - Require(attributes->Import(var->Export())); - Require(attributes->Validate(mnview)); - Require(attributes->Apply(mnview, height)); + 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; + return mnview.SetVariable(*attributes); } @@ -2570,12 +3155,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { auto nextPrice = GetAggregatePrice(mnview, obj.fixedIntervalPriceId.first, obj.fixedIntervalPriceId.second, time); - Require(nextPrice, nextPrice.msg); + if (!nextPrice) + return Res::Err(nextPrice.msg); - Require(OraclePriceFeed(mnview, obj.fixedIntervalPriceId), - "Price feed %s/%s does not belong to any oracle", - obj.fixedIntervalPriceId.first, - obj.fixedIntervalPriceId.second); + if (!OraclePriceFeed(mnview, obj.fixedIntervalPriceId)) + return Res::Err("Price feed %s/%s does not belong to any oracle", + obj.fixedIntervalPriceId.first, + obj.fixedIntervalPriceId.second); CFixedIntervalPrice fixedIntervalPrice; fixedIntervalPrice.priceFeedId = loanToken.fixedIntervalPriceId; @@ -2583,29 +3169,35 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { fixedIntervalPrice.timestamp = time; auto resSetFixedPrice = mnview.SetFixedIntervalPrice(fixedIntervalPrice); - Require(resSetFixedPrice, resSetFixedPrice.msg); + if (!resSetFixedPrice) + return Res::Err(resSetFixedPrice.msg); return mnview.SetLoanToken(loanToken, *(tokenId.val)); } Res operator()(const CLoanUpdateLoanTokenMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; - Require(HasFoundationAuth(), "tx not from foundation member!"); + if (!HasFoundationAuth()) + return Res::Err("tx not from foundation member!"); - if (height < static_cast(consensus.FortCanningGreatWorldHeight)) { - Require(obj.interest >= 0, "interest rate cannot be less than 0!"); + if (obj.interest < 0 && height < static_cast(consensus.FortCanningGreatWorldHeight)) { + return Res::Err("interest rate cannot be less than 0!"); } auto pair = mnview.GetTokenByCreationTx(obj.tokenTx); - Require(pair, "Loan token (%s) does not exist!", obj.tokenTx.GetHex()); + if (!pair) + return Res::Err("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); - Require(loanToken, "Loan token (%s) does not exist!", obj.tokenTx.GetHex()); + if (!loanToken) + return Res::Err("Loan token (%s) does not exist!", obj.tokenTx.GetHex()); if (obj.mintable != loanToken->mintable) loanToken->mintable = obj.mintable; @@ -2622,7 +3214,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (obj.mintable != (pair->second.flags & (uint8_t)CToken::TokenFlags::Mintable)) pair->second.flags ^= (uint8_t)CToken::TokenFlags::Mintable; - Require(mnview.UpdateToken(pair->second)); + res = mnview.UpdateToken(pair->second); + if (!res) + return res; if (height >= static_cast(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) { const auto &id = pair->first.v; @@ -2635,26 +3229,39 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CDataStructureV0 pairKey{AttributeTypes::Token, id, TokenKeys::FixedIntervalPriceId}; auto gv = GovVariable::Create("ATTRIBUTES"); - Require(gv, "Failed to create ATTRIBUTES Governance variable"); + if (!gv) { + return Res::Err("Failed to create ATTRIBUTES Governance variable"); + } auto var = std::dynamic_pointer_cast(gv); - Require(var, "Failed to convert ATTRIBUTES Governance variable"); + if (!var) { + return Res::Err("Failed to convert ATTRIBUTES Governance variable"); + } var->SetValue(mintEnabled, obj.mintable); var->SetValue(mintInterest, obj.interest); var->SetValue(pairKey, obj.fixedIntervalPriceId); - Require(attributes->Import(var->Export())); - Require(attributes->Validate(mnview)); - Require(attributes->Apply(mnview, height)); + 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; + return mnview.SetVariable(*attributes); } if (obj.fixedIntervalPriceId != loanToken->fixedIntervalPriceId) { - Require(OraclePriceFeed(mnview, obj.fixedIntervalPriceId), - "Price feed %s/%s does not belong to any oracle", - obj.fixedIntervalPriceId.first, - obj.fixedIntervalPriceId.second); + if (!OraclePriceFeed(mnview, obj.fixedIntervalPriceId)) + return Res::Err("Price feed %s/%s does not belong to any oracle", + obj.fixedIntervalPriceId.first, + obj.fixedIntervalPriceId.second); loanToken->fixedIntervalPriceId = obj.fixedIntervalPriceId; } @@ -2663,16 +3270,26 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CLoanSchemeMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) { + return res; + } - Require(HasFoundationAuth(), "tx not from foundation member!"); + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member!"); + } - Require(obj.ratio >= 100, "minimum collateral ratio cannot be less than 100"); + if (obj.ratio < 100) { + return Res::Err("minimum collateral ratio cannot be less than 100"); + } - Require(obj.rate >= 1000000, "interest rate cannot be less than 0.01"); + if (obj.rate < 1000000) { + return Res::Err("interest rate cannot be less than 0.01"); + } - Require(!obj.identifier.empty() && obj.identifier.length() <= 8, - "id cannot be empty or more than 8 chars long"); + if (obj.identifier.empty() || obj.identifier.length() > 8) { + return Res::Err("id cannot be empty or more than 8 chars long"); + } // Look for loan scheme which already has matching rate and ratio bool duplicateLoan = false; @@ -2687,36 +3304,45 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { return true; }); - Require(!duplicateLoan, "Loan scheme %s with same interestrate and mincolratio already exists", duplicateID); - - // 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; - }); + 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 pending on block %d", - duplicateKey.first, - duplicateKey.second); + if (duplicateLoan) { + return Res::Err("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)) - 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); + 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); + } // 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) { - Require(obj.updateHeight >= height, "Update height below current block height, set future height"); + if (obj.updateHeight < height) { + return Res::Err("Update height below current block height, set future height"); + } + return mnview.StoreDelayedLoanScheme(obj); } @@ -2729,36 +3355,64 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CDefaultLoanSchemeMessage &obj) const { - Require(CheckCustomTx()); - Require(HasFoundationAuth(), "tx not from foundation member!"); + auto res = CheckCustomTx(); + if (!res) { + return res; + } - 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 (!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); + } - if (auto currentID = mnview.GetDefaultLoanScheme()) - Require(*currentID != obj.identifier, "Loan scheme with id %s is already set as default", 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); + } - 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 { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) { + return res; + } + + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member!"); + } - Require(HasFoundationAuth(), "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"); + } - 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 (!mnview.GetLoanScheme(obj.identifier)) { + return Res::Err("Cannot find existing loan scheme with id %s", obj.identifier); + } const auto currentID = mnview.GetDefaultLoanScheme(); - Require(currentID && *currentID != obj.identifier, "Cannot destroy default loan scheme, set new default first"); + if (currentID && *currentID == obj.identifier) { + return Res::Err("Cannot destroy default loan scheme, set new default first"); + } // Update set and not updated on this block. if (obj.destroyHeight && obj.destroyHeight != height) { - Require(obj.destroyHeight >= height, "Destruction height below current block height, set future height"); + if (obj.destroyHeight < height) { + return Res::Err("Destruction height below current block height, set future height"); + } return mnview.StoreDelayedDestroyScheme(obj); } @@ -2775,52 +3429,65 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CVaultMessage &obj) const { auto vaultCreationFee = consensus.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)); + 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)); + } CVaultData vault{}; static_cast(vault) = obj; // set loan scheme to default if non provided if (obj.schemeId.empty()) { - auto defaultScheme = mnview.GetDefaultLoanScheme(); - Require(defaultScheme, "There is no default loan scheme"); - vault.schemeId = *defaultScheme; + if (auto defaultScheme = mnview.GetDefaultLoanScheme()) { + vault.schemeId = *defaultScheme; + } else { + return Res::Err("There is no default loan scheme"); + } } // loan scheme exists - Require(mnview.GetLoanScheme(vault.schemeId), "Cannot find existing loan scheme with id %s", vault.schemeId); + if (!mnview.GetLoanScheme(vault.schemeId)) { + return Res::Err("Cannot find existing loan scheme with id %s", vault.schemeId); + } // check loan scheme is not to be destroyed - auto height = mnview.GetDestroyLoanScheme(obj.schemeId); - Require(!height, "Cannot set %s as loan scheme, set to be destroyed on block %d", obj.schemeId, *height); + 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 vaultId = tx.GetHash(); return mnview.StoreVault(vaultId, vault); } Res operator()(const CCloseVaultMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // vault exists auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(!vault->isUnderLiquidation, "Cannot close vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot close vault under liquidation"); // owner auth - Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); + if (!HasAuth(vault->ownerAddress)) + return Res::Err("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); - Require(rate, "Cannot get interest rate for this token (%d)", tokenId.v); + if (!rate) { + return Res::Err("Cannot get interest rate for this token (%d)", tokenId.v); + } const auto totalInterest = TotalInterest(*rate, height); - - Require(amount + totalInterest <= 0, "Vault <%s> has loans", obj.vaultId.GetHex()); + if (amount + totalInterest > 0) { + return Res::Err("Vault <%s> has loans", obj.vaultId.GetHex()); + } if (totalInterest < 0) { TrackNegativeInterest( @@ -2830,45 +3497,54 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } CalculateOwnerRewards(obj.to); - if (auto collaterals = mnview.GetVaultCollaterals(obj.vaultId)) - for (const auto &col : collaterals->balances) - Require(mnview.AddBalance(obj.to, {col.first, col.second})); + 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; + } + } // delete all interest to vault - Require(mnview.EraseInterest(obj.vaultId, height)); + res = mnview.EraseInterest(obj.vaultId, height); + if (!res) + return res; // return half fee, the rest is burned at creation auto feeBack = consensus.vaultCreationFee / 2; - Require(mnview.AddBalance(obj.to, {DCT_ID{0}, feeBack})); - return mnview.EraseVault(obj.vaultId); + res = mnview.AddBalance(obj.to, {DCT_ID{0}, feeBack}); + return !res ? res : mnview.EraseVault(obj.vaultId); } Res operator()(const CUpdateVaultMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // vault exists auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(!vault->isUnderLiquidation, "Cannot update vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot update vault under liquidation"); // owner auth - Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); + if (!HasAuth(vault->ownerAddress)) + return Res::Err("tx must have at least one input from token owner"); // loan scheme exists const auto scheme = mnview.GetLoanScheme(obj.schemeId); - Require(scheme, "Cannot find existing loan scheme with id %s", obj.schemeId); + if (!scheme) + return Res::Err("Cannot find existing loan scheme with id %s", obj.schemeId); // loan scheme is not set to be destroyed - 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 (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); - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot update vault while any of the asset's price is invalid"); + if (!IsVaultPriceValid(mnview, obj.vaultId, height)) + return Res::Err("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) { @@ -2877,9 +3553,11 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { bool useNextPrice = i > 0, requireLivePrice = true; auto collateralsLoans = mnview.GetLoanCollaterals( obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); - Require(collateralsLoans.val->ratio() >= scheme->ratio, + 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); @@ -2890,8 +3568,11 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (const auto &[tokenId, tokenAmount] : loanTokens->balances) { const auto loanToken = mnview.GetLoanTokenByID(tokenId); assert(loanToken); - Require(mnview.IncreaseInterest( - height, obj.vaultId, obj.schemeId, tokenId, loanToken->interest, 0)); + res = + mnview.IncreaseInterest(height, obj.vaultId, obj.schemeId, tokenId, loanToken->interest, 0); + if (!res) { + return res; + } } } } @@ -2905,7 +3586,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"); } @@ -2978,74 +3659,88 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CDepositToVaultMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // owner auth - Require(HasAuth(obj.from), "tx must have at least one input from token owner"); + if (!HasAuth(obj.from)) + return Res::Err("tx must have at least one input from token owner"); // vault exists auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(!vault->isUnderLiquidation, "Cannot deposit to vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("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()) { - Require(attributes->GetValue(collateralKey, false), - "Collateral token (%d) is disabled", - obj.amount.nTokenId.v); + if (const auto attributes = mnview.GetAttributes(); !attributes->GetValue(collateralKey, false)) { + return Res::Err("Collateral token (%d) is disabled", obj.amount.nTokenId.v); } } // check balance CalculateOwnerRewards(obj.from); - 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.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.AddVaultCollateral(obj.vaultId, obj.amount)); + res = mnview.AddVaultCollateral(obj.vaultId, obj.amount); + if (!res) + return res; bool useNextPrice = false, requireLivePrice = false; auto collaterals = mnview.GetVaultCollaterals(obj.vaultId); auto collateralsLoans = mnview.GetLoanCollaterals(obj.vaultId, *collaterals, height, time, useNextPrice, requireLivePrice); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); auto scheme = mnview.GetLoanScheme(vault->schemeId); - Require(collateralsLoans.val->ratio() >= scheme->ratio, - "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + if (collateralsLoans.val->ratio() < scheme->ratio) + return Res::Err("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 { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // vault exists auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(!vault->isUnderLiquidation, "Cannot withdraw from vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot withdraw from vault under liquidation"); // owner auth - Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); + if (!HasAuth(vault->ownerAddress)) + return Res::Err("tx must have at least one input from token owner"); - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot withdraw from vault while any of the asset's price is invalid"); + if (!IsVaultPriceValid(mnview, obj.vaultId, height)) + return Res::Err("Cannot withdraw from vault while any of the asset's price is invalid"); - Require(mnview.SubVaultCollateral(obj.vaultId, obj.amount)); + res = mnview.SubVaultCollateral(obj.vaultId, obj.amount); + if (!res) + return res; auto hasDUSDLoans = false; - std::optional > > tokenDUSD; + std::optional>> tokenDUSD; if (static_cast(height) >= consensus.FortCanningRoadHeight) { tokenDUSD = mnview.GetToken("DUSD"); } @@ -3074,7 +3769,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {tokenId, subAmount}); } - Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount})); + res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount}); + if (!res) { + return res; + } TrackNegativeInterest(mnview, {tokenId, subAmount}); @@ -3086,16 +3784,21 @@ 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); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); - Require(collateralsLoans.val->ratio() >= scheme->ratio, + 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(CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio)); + res = CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio); + if (!res) + return res; } } else { return Res::Err("Cannot withdraw all collaterals as there are still active loans in this vault"); @@ -3106,59 +3809,71 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CPaybackWithCollateralMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // vault exists const auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(!vault->isUnderLiquidation, "Cannot payback vault with collateral while vault's under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot payback vault with collateral while vault's under liquidation"); // owner auth - Require(HasAuth(vault->ownerAddress), "tx must have at least one input from token owner"); + if (!HasAuth(vault->ownerAddress)) + return Res::Err("tx must have at least one input from token owner"); return PaybackWithCollateral(mnview, *vault, obj.vaultId, height, time); } Res operator()(const CLoanTakeLoanMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; const auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); - Require(!vault->isUnderLiquidation, "Cannot take loan on vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot take loan on vault under liquidation"); // vault owner auth - Require(HasAuth(vault->ownerAddress), "tx must have at least one input from vault owner"); + if (!HasAuth(vault->ownerAddress)) + return Res::Err("tx must have at least one input from vault owner"); - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot take loan while any of the asset's price in the vault is not live"); + 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"); auto collaterals = mnview.GetVaultCollaterals(obj.vaultId); - Require(collaterals, "Vault with id %s has no collaterals", obj.vaultId.GetHex()); + if (!collaterals) + return Res::Err("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)) { - Require(tokenAmount > 0, "Valid loan amount required (input: %d@%d)", tokenAmount, tokenId.v); - } + if (height >= static_cast(consensus.FortCanningGreatWorldHeight) && tokenAmount <= 0) + return Res::Err("Valid loan amount required (input: %d@%d)", tokenAmount, tokenId.v); auto loanToken = mnview.GetLoanTokenByID(tokenId); - Require(loanToken, "Loan token with id (%s) does not exist!", tokenId.ToString()); + 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->mintable, - "Loan cannot be taken on token with id (%s) as \"mintable\" is currently false", - tokenId.ToString()); if (tokenDUSD && tokenId == tokenDUSD->first) { hasDUSDLoans = true; } @@ -3199,7 +3914,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDAdd(mnview, {tokenId, loanAmountChange}); } - Require(mnview.AddLoanToken(obj.vaultId, CTokenAmount{tokenId, loanAmountChange})); + res = mnview.AddLoanToken(obj.vaultId, CTokenAmount{tokenId, loanAmountChange}); + if (!res) + return res; } else { const auto subAmount = currentLoanAmount > std::abs(loanAmountChange) ? std::abs(loanAmountChange) : currentLoanAmount; @@ -3208,63 +3925,74 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {tokenId, subAmount}); } - Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount})); + res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{tokenId, subAmount}); + if (!res) { + return res; + } } if (resetInterestToHeight) { mnview.ResetInterest(height, obj.vaultId, vault->schemeId, tokenId); } else { - Require(mnview.IncreaseInterest( - height, obj.vaultId, vault->schemeId, tokenId, loanToken->interest, loanAmountChange)); + res = mnview.IncreaseInterest( + height, obj.vaultId, vault->schemeId, tokenId, loanToken->interest, loanAmountChange); + if (!res) + return res; } const auto tokenCurrency = loanToken->fixedIntervalPriceId; auto priceFeed = mnview.GetFixedIntervalPrice(tokenCurrency); - Require(priceFeed, priceFeed.msg); + if (!priceFeed) + return Res::Err(priceFeed.msg); - Require(priceFeed.val->isLive(mnview.GetPriceDeviation()), - "No live fixed prices for %s/%s", - tokenCurrency.first, - tokenCurrency.second); + if (!priceFeed.val->isLive(mnview.GetPriceDeviation())) + return Res::Err("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) { - Require(amount >= tokenAmount, - "Value/price too high (%s/%s)", - GetDecimaleString(tokenAmount), - GetDecimaleString(price)); - } + if (price > COIN && amount < tokenAmount) + return Res::Err( + "Value/price too high (%s/%s)", GetDecimaleString(tokenAmount), GetDecimaleString(price)); + auto &totalLoans = i > 0 ? totalLoansNextPrice : totalLoansActivePrice; auto prevLoans = totalLoans; totalLoans += amount; - Require(prevLoans <= totalLoans, "Exceed maximum loans"); + if (prevLoans > totalLoans) + return Res::Err("Exceed maximum loans"); } - Require(mnview.AddMintedTokens(tokenId, tokenAmount)); + res = mnview.AddMintedTokens(tokenId, tokenAmount); + if (!res) + return res; const auto &address = !obj.to.empty() ? obj.to : vault->ownerAddress; CalculateOwnerRewards(address); - Require(mnview.AddBalance(address, CTokenAmount{tokenId, tokenAmount})); + res = mnview.AddBalance(address, CTokenAmount{tokenId, tokenAmount}); + if (!res) + return res; } 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); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); - Require(collateralsLoans.val->ratio() >= scheme->ratio, - "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + if (collateralsLoans.val->ratio() < scheme->ratio) + return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", + collateralsLoans.val->ratio(), + scheme->ratio); - Require(CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio)); + res = CollateralPctCheck(hasDUSDLoans, collateralsLoans, scheme->ratio); + if (!res) + return res; } return Res::Ok(); } @@ -3278,7 +4006,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CBalances *loan; if (id == DCT_ID{0}) { auto tokenDUSD = mnview.GetToken("DUSD"); - Require(tokenDUSD, "Loan token DUSD does not exist!"); + if (!tokenDUSD) + return Res::Err("Loan token DUSD does not exist!"); loan = &loans[tokenDUSD->first]; } else loan = &loans[id]; @@ -3289,21 +4018,27 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CLoanPaybackLoanV2Message &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; const auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Cannot find existing vault with id %s", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Cannot find existing vault with id %s", obj.vaultId.GetHex()); - Require(!vault->isUnderLiquidation, "Cannot payback loan on vault under liquidation"); + if (vault->isUnderLiquidation) + return Res::Err("Cannot payback loan on vault under liquidation"); - Require(mnview.GetVaultCollaterals(obj.vaultId), "Vault with id %s has no collaterals", obj.vaultId.GetHex()); + if (!mnview.GetVaultCollaterals(obj.vaultId)) { + return Res::Err("Vault with id %s has no collaterals", obj.vaultId.GetHex()); + } - Require(HasAuth(obj.from), "tx must have at least one input from token owner"); + if (!HasAuth(obj.from)) + return Res::Err("tx must have at least one input from token owner"); - if (static_cast(height) < consensus.FortCanningRoadHeight) { - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot payback loan while any of the asset's price is invalid"); - } + if (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"); // Handle payback with collateral special case if (static_cast(height) >= consensus.FortCanningEpilogueHeight && @@ -3317,45 +4052,46 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { for (const auto &[loanTokenId, paybackAmounts] : obj.loans) { const auto loanToken = mnview.GetLoanTokenByID(loanTokenId); - Require(loanToken, "Loan token with id (%s) does not exist!", loanTokenId.ToString()); + if (!loanToken) + return Res::Err("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)) { - Require(paybackAmount > 0, - "Valid payback amount required (input: %d@%d)", - paybackAmount, - paybackTokenId.v); + if (height >= static_cast(consensus.FortCanningGreatWorldHeight) && paybackAmount <= 0) { + return Res::Err("Valid payback amount required (input: %d@%d)", paybackAmount, paybackTokenId.v); } CAmount paybackUsdPrice{0}, loanUsdPrice{0}, penaltyPct{COIN}; auto paybackToken = mnview.GetToken(paybackTokenId); - Require(paybackToken, "Token with id (%s) does not exists", paybackTokenId.ToString()); + if (!paybackToken) + return Res::Err("Token with id (%s) does not exists", paybackTokenId.ToString()); if (loanTokenId != paybackTokenId) { - Require(IsVaultPriceValid(mnview, obj.vaultId, height), - "Cannot payback loan while any of the asset's price is invalid"); - Require(attributes, "Payback is not currently active"); + if (!IsVaultPriceValid(mnview, obj.vaultId, height)) + return Res::Err("Cannot payback loan while any of the asset's price is invalid"); + + if (!attributes) + return Res::Err("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}; - Require(attributes->GetValue(activeKey, false), - "Payback of loan via %s token is not currently active", - paybackToken->symbol); + if (!attributes->GetValue(activeKey, false)) + return Res::Err("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}; - Require(attributes->GetValue(activeKey, false), - "Payback of loan via %s token is not currently active", - paybackToken->symbol); + if (!attributes->GetValue(activeKey, false)) + return Res::Err("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); @@ -3365,7 +4101,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const CTokenCurrencyPair tokenUsdPair{paybackToken->symbol, "USD"}; bool useNextPrice{false}, requireLivePrice{true}; const auto resVal = mnview.GetValidatedIntervalPrice(tokenUsdPair, useNextPrice, requireLivePrice); - Require(resVal); + if (!resVal) + return std::move(resVal); paybackUsdPrice = MultiplyAmounts(*resVal.val, penaltyPct); @@ -3374,19 +4111,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (loanToken->symbol == "DUSD") { paybackAmount = usdAmount; - if (paybackUsdPrice > COIN) { - Require(paybackAmount >= kv.second, - "Value/price too high (%s/%s)", - GetDecimaleString(kv.second), - GetDecimaleString(paybackUsdPrice)); - } + if (paybackUsdPrice > COIN && paybackAmount < kv.second) + return Res::Err("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); - Require(resVal); + if (!resVal) + return std::move(resVal); loanUsdPrice = *resVal.val; @@ -3395,16 +4131,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } const auto loanAmounts = mnview.GetLoanTokens(obj.vaultId); - Require(loanAmounts, "There are no loans on this vault (%s)!", obj.vaultId.GetHex()); + if (!loanAmounts) + return Res::Err("There are no loans on this vault (%s)!", obj.vaultId.GetHex()); - Require(loanAmounts->balances.count(loanTokenId), - "There is no loan on token (%s) in this vault!", - loanToken->symbol); + if (!loanAmounts->balances.count(loanTokenId)) + return Res::Err("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); - Require(rate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); + if (!rate) + return Res::Err("Cannot get interest rate for this token (%s)!", loanToken->symbol); auto subInterest = TotalInterest(*rate, height); @@ -3428,12 +4165,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { TrackDUSDSub(mnview, {loanTokenId, subLoan}); } - Require(mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan})); + res = mnview.SubLoanToken(obj.vaultId, CTokenAmount{loanTokenId, subLoan}); + if (!res) + return res; // Eraseinterest. On subInterest is nil interest ITH and IPB will be updated, if // subInterest is negative or IPB is negative and subLoan is equal to the loan amount // then IPB will be updated and ITH will be wiped. - Require(mnview.DecreaseInterest( + res = mnview.DecreaseInterest( height, obj.vaultId, vault->schemeId, @@ -3441,14 +4180,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { subLoan, subInterest < 0 || (rate->interestPerBlock.negative && subLoan == currentLoanAmount) ? std::numeric_limits::max() - : subInterest)); + : subInterest); + if (!res) + return res; if (height >= static_cast(consensus.FortCanningMuseumHeight) && subLoan < currentLoanAmount && height < static_cast(consensus.FortCanningGreatWorldHeight)) { auto newRate = mnview.GetInterestRate(obj.vaultId, loanTokenId, height); - Require(newRate, "Cannot get interest rate for this token (%s)!", loanToken->symbol); + if (!newRate) + return Res::Err("Cannot get interest rate for this token (%s)!", loanToken->symbol); - Require(newRate->interestPerBlock.amount != 0, + if (newRate->interestPerBlock.amount == 0) + return Res::Err( "Cannot payback this amount of loan for %s, either payback full amount or less than this " "amount!", loanToken->symbol); @@ -3457,7 +4200,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { CalculateOwnerRewards(obj.from); if (paybackTokenId == loanTokenId) { - Require(mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest)); + res = mnview.SubMintedTokens(loanTokenId, subInterest > 0 ? subLoan : subLoan + subInterest); + if (!res) + return res; // If interest was negative remove it from sub amount if (height >= static_cast(consensus.FortCanningEpilogueHeight) && subInterest < 0) @@ -3476,7 +4221,9 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { "CLoanPaybackLoanMessage(): Sub loan from balance - %lld, height - %d\n", subLoan, height); - Require(mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan})); + res = mnview.SubBalance(obj.from, CTokenAmount{loanTokenId, subLoan}); + if (!res) + return res; } // burn interest Token->USD->DFI->burnAddress @@ -3486,8 +4233,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { loanToken->symbol, subInterest, height); - Require( - SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height)); + res = + SwapToDFIorDUSD(mnview, loanTokenId, subInterest, obj.from, consensus.burnAddress, height); } } else { CAmount subInToken; @@ -3536,7 +4283,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { paybackToken->symbol, height); - Require(TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress)); + res = TransferTokenBalance(paybackTokenId, subInToken, obj.from, consensus.burnAddress); } else { CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackTokens}; auto balances = attributes->GetValue(liveKey, CTokenPayback{}); @@ -3558,15 +4305,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { AttributeTypes::Param, ParamIDs::DFIP2206A, DFIPKeys::DUSDLoanBurn}; auto directLoanBurn = attributes->GetValue(directBurnKey, false); - Require(SwapToDFIorDUSD(mnview, - paybackTokenId, - subInToken, - obj.from, - consensus.burnAddress, - height, - !directLoanBurn)); + res = SwapToDFIorDUSD(mnview, + paybackTokenId, + subInToken, + obj.from, + consensus.burnAddress, + height, + !directLoanBurn); } } + + if (!res) + return res; } } @@ -3574,39 +4324,48 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } Res operator()(const CAuctionBidMessage &obj) const { - Require(CheckCustomTx()); + auto res = CheckCustomTx(); + if (!res) + return res; // owner auth - Require(HasAuth(obj.from), "tx must have at least one input from token owner"); + if (!HasAuth(obj.from)) + return Res::Err("tx must have at least one input from token owner"); // vault exists auto vault = mnview.GetVault(obj.vaultId); - Require(vault, "Vault <%s> not found", obj.vaultId.GetHex()); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); // vault under liquidation - Require(vault->isUnderLiquidation, "Cannot bid to vault which is not under liquidation"); + if (!vault->isUnderLiquidation) + return Res::Err("Cannot bid to vault which is not under liquidation"); auto data = mnview.GetAuction(obj.vaultId, height); - Require(data, "No auction data to vault %s", obj.vaultId.GetHex()); + if (!data) + return Res::Err("No auction data to vault %s", obj.vaultId.GetHex()); auto batch = mnview.GetAuctionBatch({obj.vaultId, obj.index}); - Require(batch, "No batch to vault/index %s/%d", obj.vaultId.GetHex(), obj.index); + if (!batch) + return Res::Err("No batch to vault/index %s/%d", obj.vaultId.GetHex(), obj.index); - Require(obj.amount.nTokenId == batch->loanAmount.nTokenId, "Bid token does not match auction one"); + if (obj.amount.nTokenId != batch->loanAmount.nTokenId) + return Res::Err("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); - Require(amount <= obj.amount.nValue, - "First bid should include liquidation penalty of %d%%", - data->liquidationPenalty * 100 / COIN); + if (amount > obj.amount.nValue) + return Res::Err("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)); - Require(amount <= obj.amount.nValue, "Bid override should be at least 1%% higher than current one"); + if (amount > obj.amount.nValue) + return Res::Err("Bid override should be at least 1%% higher than current one"); if (static_cast(height) >= consensus.FortCanningMuseumHeight && obj.amount.nValue == bid->second.nValue) @@ -3618,8 +4377,8 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { } // check balance CalculateOwnerRewards(obj.from); - Require(mnview.SubBalance(obj.from, obj.amount)); - return mnview.StoreAuctionBid({obj.vaultId, obj.index}, {obj.from, obj.amount}); + res = mnview.SubBalance(obj.from, obj.amount); + return !res ? res : mnview.StoreAuctionBid({obj.vaultId, obj.index}, {obj.from, obj.amount}); } Res operator()(const CCreatePropMessage &obj) const { @@ -3913,7 +4672,6 @@ 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))) { @@ -3997,7 +4755,9 @@ ResVal ApplyAnchorRewardTx(CCustomCSView &mnview, const uint256 &prevStakeModifier, const std::vector &metadata, const Consensus::Params &consensusParams) { - Require(height < consensusParams.DakotaHeight, "Old anchor TX type after Dakota fork. Height %d", height); + if (height >= consensusParams.DakotaHeight) { + return Res::Err("Old anchor TX type after Dakota fork. Height %d", height); + } CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); CAnchorFinalizationMessage finMsg; @@ -4076,7 +4836,9 @@ ResVal ApplyAnchorRewardTxPlus(CCustomCSView &mnview, int height, const std::vector &metadata, const Consensus::Params &consensusParams) { - Require(height >= consensusParams.DakotaHeight, "New anchor TX type before Dakota fork. Height %d", height); + if (height < consensusParams.DakotaHeight) { + return Res::Err("New anchor TX type before Dakota fork. Height %d", height); + } CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); CAnchorFinalizationMessagePlus finMsg; @@ -4103,33 +4865,49 @@ ResVal ApplyAnchorRewardTxPlus(CCustomCSView &mnview, } auto quorum = GetMinAnchorQuorum(*team); - 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); + 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); + } // Make sure anchor block height and hash exist in chain. - 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()); + 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()); + } + // check reward sum const auto cbValues = tx.GetValuesOut(); - Require(cbValues.size() == 1 && cbValues.begin()->first == DCT_ID{0}, "anchor reward should be paid in DFI only"); + 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"); const auto anchorReward = mnview.GetCommunityBalance(CommunityAccountType::AnchorReward); - Require(cbValues.begin()->second == anchorReward, - "anchor pays wrong amount (actual=%d vs expected=%d)", - cbValues.begin()->second, - 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); + } CTxDestination destination = finMsg.rewardKeyType == 1 ? CTxDestination(PKHash(finMsg.rewardKeyID)) : CTxDestination(WitnessV0KeyHash(finMsg.rewardKeyID)); - Require(tx.vout[1].scriptPubKey == GetScriptForDestination(destination), "anchor pay destination is incorrect"); + if (tx.vout[1].scriptPubKey != GetScriptForDestination(destination)) { + return Res::ErrDbg("bad-ar-dest", "anchor pay destination is incorrect"); + } LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: txid=%s fund=%s change=%s\n", @@ -4156,7 +4934,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}; @@ -4186,8 +4964,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; @@ -4257,7 +5035,6 @@ std::vector > CPoolSwap::CalculatePoolPaths(CCustomCSView &v } } } - return true; }, {0}); @@ -4271,12 +5048,15 @@ std::vector > CPoolSwap::CalculatePoolPaths(CCustomCSView &v // 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(); } - Require(obj.amountFrom > 0, "Input amount should be positive"); + if (obj.amountFrom <= 0) { + return Res::Err("Input amount should be positive"); + } if (height >= static_cast(Params().GetConsensus().FortCanningHillHeight) && poolIDs.size() > MAX_POOL_SWAPS) { @@ -4286,10 +5066,12 @@ 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); - Require(poolPair, "Cannot find the pool pair."); + if (!poolPair) { + return Res::Err("Cannot find the pool pair."); + } // Add single swap pool to vector for loop poolIDs.push_back(poolPair->first); @@ -4325,7 +5107,9 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo } else // Or get pools from IDs provided for composite swap { pool = view.GetPoolPair(currentID); - Require(pool, "Cannot find the pool pair."); + if (!pool) { + return Res::Err("Cannot find the pool pair."); + } } // Check if last pool swap @@ -4334,11 +5118,12 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo const auto swapAmount = swapAmountResult; if (height >= static_cast(Params().GetConsensus().FortCanningHillHeight) && lastSwap) { - 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 (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"); + } } if (view.AreTokensLocked({pool->idTokenA.v, pool->idTokenB.v})) { @@ -4474,7 +5259,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView &view, std::vector poolIDs, boo // Assign to result for loop testing best pool swap result result = swapAmountResult.nValue; - return Res::Ok(); + return poolResult; } Res SwapToDFIorDUSD(CCustomCSView &mnview, @@ -4495,14 +5280,18 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, auto poolSwap = CPoolSwap(obj, height); auto token = mnview.GetToken(tokenId); - Require(token, "Cannot find token with id %s!", tokenId.ToString()); + if (!token) + return Res::Err("Cannot find token with id %s!", tokenId.ToString()); // TODO: Optimize double look up later when first token is DUSD. auto dUsdToken = mnview.GetToken("DUSD"); - Require(dUsdToken, "Cannot find token DUSD"); + if (!dUsdToken) + return Res::Err("Cannot find token DUSD"); const auto attributes = mnview.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) { + return Res::Err("Attributes unavailable"); + } CDataStructureV0 directBurnKey{AttributeTypes::Param, ParamIDs::DFIP2206A, DFIPKeys::DUSDInterestBurn}; // Direct swap from DUSD to DFI as defined in the CPoolSwapMessage. @@ -4511,7 +5300,9 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, // direct burn dUSD CTokenAmount dUSD{dUsdToken->first, amount}; - Require(mnview.SubBalance(from, dUSD)); + auto res = mnview.SubBalance(from, dUSD); + if (!res) + return res; return mnview.AddBalance(to, dUSD); } else @@ -4520,10 +5311,12 @@ Res SwapToDFIorDUSD(CCustomCSView &mnview, } auto pooldUSDDFI = mnview.GetPoolPair(dUsdToken->first, DCT_ID{0}); - Require(pooldUSDDFI, "Cannot find pool pair DUSD-DFI!"); + if (!pooldUSDDFI) + return Res::Err("Cannot find pool pair DUSD-DFI!"); auto poolTokendUSD = mnview.GetPoolPair(tokenId, dUsdToken->first); - Require(poolTokendUSD, "Cannot find pool pair %s-DUSD!", token->symbol); + if (!poolTokendUSD) + return Res::Err("Cannot find pool pair %s-DUSD!", token->symbol); if (to == Params().GetConsensus().burnAddress && !forceLoanSwap && attributes->GetValue(directBurnKey, false)) { obj.idTokenTo = dUsdToken->first; @@ -4590,30 +5383,38 @@ Res PaybackWithCollateral(CCustomCSView &view, uint32_t height, uint64_t time) { const auto attributes = view.GetAttributes(); - Require(attributes, "Attributes unavailable"); + if (!attributes) + return Res::Err("Attributes unavailable"); const auto dUsdToken = view.GetToken("DUSD"); - Require(dUsdToken, "Cannot find token DUSD"); + if (!dUsdToken) + return Res::Err("Cannot find token DUSD"); CDataStructureV0 activeKey{AttributeTypes::Token, dUsdToken->first.v, TokenKeys::LoanPaybackCollateral}; - Require(attributes->GetValue(activeKey, false), "Payback of DUSD loan with collateral is not currently active"); + if (!attributes->GetValue(activeKey, false)) + return Res::Err("Payback of DUSD loan with collateral is not currently active"); const auto collateralAmounts = view.GetVaultCollaterals(vaultId); - Require(collateralAmounts, "Vault has no collaterals"); - - Require(collateralAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD collaterals"); - + 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"); + } const auto &collateralDUSD = collateralAmounts->balances.at(dUsdToken->first); const auto loanAmounts = view.GetLoanTokens(vaultId); - Require(loanAmounts, "Vault has no loans"); - - Require(loanAmounts->balances.count(dUsdToken->first), "Vault does not have any DUSD loans"); - + 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"); + } const auto &loanDUSD = loanAmounts->balances.at(dUsdToken->first); const auto rate = view.GetInterestRate(vaultId, dUsdToken->first, height); - Require(rate, "Cannot get interest rate for this token (DUSD)!"); + if (!rate) + return Res::Err("Cannot get interest rate for this token (DUSD)!"); const auto subInterest = TotalInterest(*rate, height); Res res{}; @@ -4625,9 +5426,13 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subInterest > collateralDUSD) { subCollateralAmount = collateralDUSD; - Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); + res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); + if (!res) + return res; - Require(view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount)); + res = view.DecreaseInterest(height, vaultId, vault.schemeId, dUsdToken->first, 0, subCollateralAmount); + if (!res) + return res; burnAmount = subCollateralAmount; } else { @@ -4644,11 +5449,15 @@ Res PaybackWithCollateral(CCustomCSView &view, if (subLoanAmount > 0) { TrackDUSDSub(view, {dUsdToken->first, subLoanAmount}); - Require(view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount})); + res = view.SubLoanToken(vaultId, {dUsdToken->first, subLoanAmount}); + if (!res) + return res; } if (subCollateralAmount > 0) { - Require(view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount})); + res = view.SubVaultCollateral(vaultId, {dUsdToken->first, subCollateralAmount}); + if (!res) + return res; } view.ResetInterest(height, vaultId, vault.schemeId, dUsdToken->first); @@ -4656,7 +5465,9 @@ Res PaybackWithCollateral(CCustomCSView &view, } if (burnAmount > 0) { - Require(view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount})); + res = view.AddBalance(Params().GetConsensus().burnAddress, {dUsdToken->first, burnAmount}); + if (!res) + return res; } else { TrackNegativeInterest(view, {dUsdToken->first, std::abs(burnAmount)}); } @@ -4664,26 +5475,29 @@ Res PaybackWithCollateral(CCustomCSView &view, // Guard against liquidation const auto collaterals = view.GetVaultCollaterals(vaultId); const auto loans = view.GetLoanTokens(vaultId); - if (loans) - Require(collaterals, "Vault cannot have loans without collaterals"); + if (!collaterals && loans) + return Res::Err("Vault cannot have loans without collaterals"); auto collateralsLoans = view.GetLoanCollaterals(vaultId, *collaterals, height, time); - Require(collateralsLoans); + if (!collateralsLoans) + return std::move(collateralsLoans); // The check is required to do a ratio check safe guard, or the vault of ratio is unreliable. // This can later be removed, if all edge cases of price deviations and max collateral factor for DUSD (1.5 // currently) can be tested for economical stability. Taking the safer approach for now. - Require(IsVaultPriceValid(view, vaultId, height), - "Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); + if (!IsVaultPriceValid(view, vaultId, height)) + return Res::Err("Cannot payback vault with non-DUSD assets while any of the asset's price is invalid"); const auto scheme = view.GetLoanScheme(vault.schemeId); - Require(collateralsLoans.val->ratio() >= scheme->ratio, - "Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", - collateralsLoans.val->ratio(), - scheme->ratio); + if (collateralsLoans.val->ratio() < scheme->ratio) + return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", + collateralsLoans.val->ratio(), + scheme->ratio); if (subCollateralAmount > 0) { - Require(view.SubMintedTokens(dUsdToken->first, subCollateralAmount)); + res = view.SubMintedTokens(dUsdToken->first, subCollateralAmount); + if (!res) + return res; } return Res::Ok(); diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 05346d3f3a..4e12ee24bd 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -36,7 +36,7 @@ class CCustomTxVisitor { const Consensus::Params &consensus); protected: - Res HasAuth(const CScript &auth) const; + bool HasAuth(const CScript &auth) const; Res HasCollateralAuth(const uint256 &collateralTx) const; Res HasFoundationAuth() const; Res CheckMasternodeCreationTx() const; @@ -341,40 +341,27 @@ struct CBurnTokensMessage { } }; -struct CGovernanceMessage { - std::unordered_map> govs; +struct CCreatePoolPairMessage { + CPoolPairMessage poolPair; + std::string pairSymbol; + CBalances rewards; +}; - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - std::string name; - while(!s.empty()) { - s >> name; - auto& gov = govs[name]; - auto var = GovVariable::Create(name); - if (!var) break; - s >> *var; - gov = std::move(var); - } - } +struct CUpdatePoolPairMessage { + DCT_ID poolId; + bool status; + CAmount commission; + CScript ownerAddress; + CBalances rewards; +}; + +struct CGovernanceMessage { + std::set> govs; }; struct CGovernanceHeightMessage { - std::string govName; std::shared_ptr govVar; uint32_t startHeight; - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - if (!s.empty()) { - s >> govName; - if ((govVar = GovVariable::Create(govName))) { - s >> *govVar; - s >> startHeight; - } - } - } }; struct CGovernanceUnsetMessage { diff --git a/src/masternodes/oracles.cpp b/src/masternodes/oracles.cpp index d7aef45a50..84641fd01c 100644 --- a/src/masternodes/oracles.cpp +++ b/src/masternodes/oracles.cpp @@ -13,7 +13,9 @@ 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) { - Require(SupportsPair(token, currency), "token <%s> - currency <%s> is not allowed", token, currency); + if (!SupportsPair(token, currency)) { + return Res::Err("token <%s> - currency <%s> is not allowed", token, currency); + } tokenPrices[token][currency] = std::make_pair(amount, timestamp); @@ -21,22 +23,30 @@ Res COracle::SetTokenPrice(const std::string &token, const std::string ¤cy } ResVal COracle::GetTokenPrice(const std::string &token, const std::string ¤cy) { - Require(SupportsPair(token, currency), "token <%s> - currency <%s> is not allowed", token, currency); + if (!SupportsPair(token, currency)) { + return Res::Err("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) { - Require(WriteBy(oracleId, oracle), "failed to appoint the new oracle <%s>", oracleId.GetHex()); + if (!WriteBy(oracleId, oracle)) { + return Res::Err("failed to appoint the new oracle <%s>", oracleId.GetHex()); + } return Res::Ok(); } Res COracleView::UpdateOracle(const COracleId &oracleId, COracle &&newOracle) { COracle oracle; - Require(ReadBy(oracleId, oracle), "oracle <%s> not found", oracleId.GetHex()); + if (!ReadBy(oracleId, oracle)) { + return Res::Err("oracle <%s> not found", oracleId.GetHex()); + } - Require(newOracle.tokenPrices.empty(), "oracle <%s> has token prices on update", oracleId.GetHex()); + if (!newOracle.tokenPrices.empty()) { + return Res::Err("oracle <%s> has token prices on update", oracleId.GetHex()); + } oracle.weightage = newOracle.weightage; oracle.oracleAddress = std::move(newOracle.oracleAddress); @@ -57,39 +67,55 @@ Res COracleView::UpdateOracle(const COracleId &oracleId, COracle &&newOracle) { oracle.availablePairs = std::move(newOracle.availablePairs); // no need to update oracles list - Require(WriteBy(oracleId, oracle), "failed to save oracle <%s>", oracleId.GetHex()); + if (!WriteBy(oracleId, oracle)) { + return Res::Err("failed to save oracle <%s>", oracleId.GetHex()); + } return Res::Ok(); } Res COracleView::RemoveOracle(const COracleId &oracleId) { - Require(ExistsBy(oracleId), "oracle <%s> not found", oracleId.GetHex()); + if (!ExistsBy(oracleId)) { + return Res::Err("oracle <%s> not found", oracleId.GetHex()); + } // remove oracle - Require(EraseBy(oracleId), "failed to remove oracle <%s>", oracleId.GetHex()); + if (!EraseBy(oracleId)) { + return Res::Err("failed to remove oracle <%s>", oracleId.GetHex()); + } return Res::Ok(); } Res COracleView::SetOracleData(const COracleId &oracleId, int64_t timestamp, const CTokenPrices &tokenPrices) { COracle oracle; - Require(ReadBy(oracleId, oracle), "failed to read oracle %s from database", oracleId.GetHex()); + if (!ReadBy(oracleId, oracle)) { + return Res::Err("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; - Require(oracle.SetTokenPrice(token, currency, price.second, timestamp)); + auto res = oracle.SetTokenPrice(token, currency, price.second, timestamp); + if (!res.ok) { + return res; + } } } - Require(WriteBy(oracleId, oracle), "failed to store oracle %s to database", oracleId.GetHex()); + if (!WriteBy(oracleId, oracle)) { + return Res::Err("failed to store oracle %s to database", oracleId.GetHex()); + } + return Res::Ok(); } ResVal COracleView::GetOracleData(const COracleId &oracleId) const { COracle oracle; - Require(ReadBy(oracleId, oracle), "oracle <%s> not found", oracleId.GetHex()); + if (!ReadBy(oracleId, oracle)) { + return Res::Err("oracle <%s> not found", oracleId.GetHex()); + } return ResVal(oracle, Res::Ok()); } @@ -105,11 +131,11 @@ bool CFixedIntervalPrice::isLive(const CAmount deviationThreshold) const { } Res COracleView::SetFixedIntervalPrice(const CFixedIntervalPrice &fixedIntervalPrice) { - Require(WriteBy(fixedIntervalPrice.priceFeedId, fixedIntervalPrice), - "failed to set new price feed <%s/%s>", - fixedIntervalPrice.priceFeedId.first, - fixedIntervalPrice.priceFeedId.second); - + if (!WriteBy(fixedIntervalPrice.priceFeedId, fixedIntervalPrice)) { + return Res::Err("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__, @@ -123,10 +149,10 @@ Res COracleView::SetFixedIntervalPrice(const CFixedIntervalPrice &fixedIntervalP ResVal COracleView::GetFixedIntervalPrice(const CTokenCurrencyPair &fixedIntervalPriceId) { CFixedIntervalPrice fixedIntervalPrice; - Require(ReadBy(fixedIntervalPriceId, fixedIntervalPrice), - "fixedIntervalPrice with id <%s/%s> not found", - fixedIntervalPriceId.first, - fixedIntervalPriceId.second); + if (!ReadBy(fixedIntervalPriceId, fixedIntervalPrice)) { + return Res::Err( + "fixedIntervalPrice with id <%s/%s> not found", fixedIntervalPriceId.first, fixedIntervalPriceId.second); + } DCT_ID firstID{}, secondID{}; const auto firstToken = GetTokenGuessId(fixedIntervalPriceId.first, firstID); @@ -141,7 +167,9 @@ ResVal COracleView::GetFixedIntervalPrice(const CTokenCurre loanTokens.insert(secondID.v); } - Require(!AreTokensLocked(loanTokens), "Fixed interval price currently disabled due to locked token"); + if (AreTokensLocked(loanTokens)) { + return Res::Err("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 b5954e1701..961331f05b 100644 --- a/src/masternodes/poolpairs.cpp +++ b/src/masternodes/poolpairs.cpp @@ -69,12 +69,16 @@ ReturnType ReadValueAt(CPoolPairView *poolView, const PoolHeightKey &poolKey) { } Res CPoolPairView::SetPoolPair(DCT_ID const &poolId, uint32_t height, const CPoolPair &pool) { - Require(pool.idTokenA != pool.idTokenB, "Error: tokens IDs are the same."); + if (pool.idTokenA == pool.idTokenB) { + return Res::Err("Error: tokens IDs are the same."); + } + auto poolPairByID = GetPoolPair(poolId); auto poolIdByTokens = ReadBy(ByPairKey{pool.idTokenA, pool.idTokenB}); - auto mismatch = (!poolPairByID && poolIdByTokens) || (poolPairByID && !poolIdByTokens); - Require(!mismatch, "Error, there is already a poolpair with same tokens, but different poolId"); + if ((!poolPairByID && poolIdByTokens) || (poolPairByID && !poolIdByTokens)) { + return Res::Err("Error, there is already a poolpair with same tokens, but different poolId"); + } // create new if (!poolPairByID && !poolIdByTokens) { @@ -85,7 +89,9 @@ Res CPoolPairView::SetPoolPair(DCT_ID const &poolId, uint32_t height, const CPoo return Res::Ok(); } - Require(poolId == *poolIdByTokens, "Error, PoolID is incorrect"); + if (poolId != *poolIdByTokens) { + return Res::Err("Error, PoolID is incorrect"); + } auto poolPairByTokens = ReadBy(poolId); assert(poolPairByTokens); @@ -116,7 +122,9 @@ Res CPoolPairView::UpdatePoolPair(DCT_ID const &poolId, const CScript &ownerAddress, const CBalances &rewards) { auto poolPair = GetPoolPair(poolId); - Require(poolPair, "Pool with poolId %s does not exist", poolId.ToString()); + if (!poolPair) { + return Res::Err("Pool with poolId %s does not exist", poolId.ToString()); + } CPoolPair &pool = poolPair.value(); @@ -125,7 +133,9 @@ Res CPoolPairView::UpdatePoolPair(DCT_ID const &poolId, } if (commission >= 0) { // default/not set is -1 - Require(commission <= COIN, "commission > 100%%"); + if (commission > COIN) { + return Res::Err("commission > 100%%"); + } pool.commission = commission; } @@ -340,14 +350,18 @@ Res CPoolPair::AddLiquidity(CAmount amountA, std::function onMint, bool slippageProtection) { // instead of assertion due to tests - Require(amountA > 0 && amountB > 0, "amounts should be positive"); + if (amountA <= 0 || amountB <= 0) { + return Res::Err("amounts should be positive"); + } CAmount liquidity{0}; if (totalLiquidity == 0) { - liquidity = (arith_uint256(amountA) * amountB) + liquidity = (CAmount)(arith_uint256(amountA) * arith_uint256(amountB)) .sqrt() .GetLow64(); // sure this is below std::numeric_limits::max() due to sqrt natue - Require(liquidity > MINIMUM_LIQUIDITY, "liquidity too low"); + if (liquidity <= MINIMUM_LIQUIDITY) { // ensure that it'll be non-zero + return Res::Err("liquidity too low"); + } liquidity -= MINIMUM_LIQUIDITY; // MINIMUM_LIQUIDITY is a hack for non-zero division totalLiquidity = MINIMUM_LIQUIDITY; @@ -356,26 +370,33 @@ Res CPoolPair::AddLiquidity(CAmount amountA, CAmount liqB = (arith_uint256(amountB) * arith_uint256(totalLiquidity) / reserveB).GetLow64(); liquidity = std::min(liqA, liqB); - Require(liquidity > 0, "amounts too low, zero liquidity"); + if (liquidity == 0) { + return Res::Err("amounts too low, zero liquidity"); + } if (slippageProtection) { - Require((std::max(liqA, liqB) - liquidity) * 100 / liquidity < 3, - "Exceeds max ratio slippage protection of 3%%"); + if ((std::max(liqA, liqB) - liquidity) * 100 / liquidity >= 3) { + return Res::Err("Exceeds max ratio slippage protection of 3%%"); + } } } // increasing totalLiquidity auto resTotal = SafeAdd(totalLiquidity, liquidity); - Require(resTotal, "can't add %d to totalLiquidity: %s", liquidity, resTotal.msg); - totalLiquidity = resTotal; + if (!resTotal.ok) { + return Res::Err("can't add %d to totalLiquidity: %s", liquidity, resTotal.msg); + } + totalLiquidity = *resTotal.val; // increasing reserves auto resA = SafeAdd(reserveA, amountA); auto resB = SafeAdd(reserveB, amountB); - Require(resA && resB, "overflow when adding to reserves"); - - reserveA = resA; - reserveB = resB; + if (resA.ok && resB.ok) { + reserveA = *resA.val; + reserveB = *resB.val; + } else { + return Res::Err("overflow when adding to reserves"); + } return onMint(liquidity); } @@ -384,7 +405,9 @@ Res CPoolPair::RemoveLiquidity(CAmount liqAmount, std::function 0 && liqAmount < totalLiquidity, "incorrect liquidity"); + if (liqAmount <= 0 || liqAmount >= totalLiquidity) { + return Res::Err("incorrect liquidity"); + } CAmount resAmountA, resAmountB; resAmountA = (arith_uint256(liqAmount) * arith_uint256(reserveA) / totalLiquidity).GetLow64(); @@ -403,11 +426,12 @@ Res CPoolPair::Swap(CTokenAmount in, const std::pair &asymmetricFee, std::function onTransfer, int height) { - Require(in.nTokenId == idTokenA || in.nTokenId == idTokenB, - "Error, input token ID (" + in.nTokenId.ToString() + ") doesn't match pool tokens (" + idTokenA.ToString() + - "," + idTokenB.ToString() + ")"); + 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(status, "Pool trading is turned off!"); + if (!status) + return Res::Err("Pool trading is turned off!"); const bool forward = in.nTokenId == idTokenA; auto &reserveF = forward ? reserveA : reserveB; @@ -415,14 +439,16 @@ 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 - Require(reserveA >= SLOPE_SWAP_RATE && reserveB >= SLOPE_SWAP_RATE, "Lack of liquidity."); + if (reserveA < SLOPE_SWAP_RATE || reserveB < SLOPE_SWAP_RATE) + return Res::Err("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); @@ -435,14 +461,18 @@ Res CPoolPair::Swap(CTokenAmount in, } CTokenAmount dexfeeInAmount{in.nTokenId, 0}; - if (dexfeeInPct > 0 && poolInFee(forward, asymmetricFee)) { - Require(dexfeeInPct <= COIN, "Dex fee input percentage over 100%%"); + if (dexfeeInPct > COIN) { + return Res::Err("Dex fee input percentage over 100%%"); + } dexfeeInAmount.nValue = MultiplyAmounts(in.nValue, dexfeeInPct); in.nValue -= dexfeeInAmount.nValue; } - Require(SafeAdd(reserveF, in.nValue), "Swapping will lead to pool's reserve overflow"); + auto checkRes = SafeAdd(reserveF, in.nValue); + if (!checkRes) { + return Res::Err("Swapping will lead to pool's reserve overflow"); + } CAmount result = slopeSwap(in.nValue, reserveF, reserveT, height); @@ -651,7 +681,9 @@ inline CAmount PoolRewardPerBlock(CAmount dailyReward, CAmount rewardPct) { } Res CPoolPairView::SetRewardPct(DCT_ID const &poolId, uint32_t height, CAmount rewardPct) { - Require(HasPoolPair(poolId), "No such pool pair"); + if (!HasPoolPair(poolId)) { + return Res::Err("No such pool pair"); + } WriteBy(poolId, rewardPct); if (auto dailyReward = ReadBy(DCT_ID{})) { WriteBy(PoolHeightKey{poolId, height}, PoolRewardPerBlock(*dailyReward, rewardPct)); @@ -660,7 +692,9 @@ Res CPoolPairView::SetRewardPct(DCT_ID const &poolId, uint32_t height, CAmount r } Res CPoolPairView::SetRewardLoanPct(DCT_ID const &poolId, uint32_t height, CAmount rewardLoanPct) { - Require(HasPoolPair(poolId), "No such pool pair"); + if (!HasPoolPair(poolId)) { + return Res::Err("No such pool pair"); + } WriteBy(poolId, rewardLoanPct); if (auto dailyReward = ReadBy(DCT_ID{})) { WriteBy(PoolHeightKey{poolId, height}, PoolRewardPerBlock(*dailyReward, rewardLoanPct)); @@ -714,7 +748,9 @@ void CPoolPairView::ForEachPoolShare(std::function= 0 && feePct <= COIN, "Token dex fee should be in percentage"); + if (feePct < 0 || feePct > COIN) { + return Res::Err("Token dex fee should be in percentage"); + } WriteBy(std::make_pair(poolId, tokenId), uint32_t(feePct)); return Res::Ok(); } diff --git a/src/masternodes/poolpairs.h b/src/masternodes/poolpairs.h index b7d2b586ae..bb52a90dda 100644 --- a/src/masternodes/poolpairs.h +++ b/src/masternodes/poolpairs.h @@ -80,7 +80,7 @@ struct CPoolSwapMessageV2 { } }; -struct CPoolPairMessageBase { +struct CPoolPairMessage { DCT_ID idTokenA, idTokenB; CAmount commission; // comission %% for traders CScript ownerAddress; @@ -98,44 +98,14 @@ struct CPoolPairMessageBase { } }; -struct CCreatePoolPairMessage : public CPoolPairMessageBase { - std::string pairSymbol; - CBalances rewards; - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITEAS(CPoolPairMessageBase, *this); - READWRITE(pairSymbol); - if (!s.empty()) - READWRITE(rewards); - } -}; - -struct CUpdatePoolPairMessage { - DCT_ID poolId; - bool status; - CAmount commission; - CScript ownerAddress; - CBalances rewards; - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(poolId.v); - READWRITE(status); - READWRITE(commission); - READWRITE(ownerAddress); - if (!s.empty()) - READWRITE(rewards); - } -}; - -class CPoolPair : public CPoolPairMessageBase { +class CPoolPair : public CPoolPairMessage { public: static const CAmount MINIMUM_LIQUIDITY = 1000; static const CAmount SLOPE_SWAP_RATE = 1000; static const uint32_t PRECISION = (uint32_t)COIN; // or just PRECISION_BITS for "<<" and ">>" + CPoolPair(const CPoolPairMessage &msg = {}) + : CPoolPairMessage(msg) {} + virtual ~CPoolPair() = default; // temporary values, not serialized CAmount reserveA = 0; @@ -188,7 +158,7 @@ class CPoolPair : public CPoolPairMessageBase { if (!ser_action.ForRead()) ioProofer(); - READWRITEAS(CPoolPairMessageBase, *this); + READWRITEAS(CPoolPairMessage, *this); READWRITE(rewards); READWRITE(creationTx); READWRITE(creationHeight); diff --git a/src/masternodes/res.h b/src/masternodes/res.h index c8b4a3b775..bd75f28045 100644 --- a/src/masternodes/res.h +++ b/src/masternodes/res.h @@ -1,10 +1,9 @@ #ifndef DEFI_MASTERNODES_RES_H #define DEFI_MASTERNODES_RES_H -#include +#include #include #include -#include struct Res { bool ok; @@ -37,16 +36,6 @@ 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, {}}; } }; @@ -78,11 +67,6 @@ struct ResVal : public Res { return *val; } - const T *operator->() const { - assert(ok); - return &(*val); - } - T &operator*() { assert(ok); return *val; @@ -104,28 +88,4 @@ 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_customtx.cpp b/src/masternodes/rpc_customtx.cpp index 281595cedb..ececf55ff0 100644 --- a/src/masternodes/rpc_customtx.cpp +++ b/src/masternodes/rpc_customtx.cpp @@ -195,13 +195,13 @@ class CCustomTxRpcVisitor { rpcInfo.pushKV("creationTx", tx.GetHash().GetHex()); if (auto tokenPair = mnview.GetTokenByCreationTx(tx.GetHash())) tokenInfo(tokenPair->second); - if (auto tokenA = mnview.GetToken(obj.idTokenA)) + if (auto tokenA = mnview.GetToken(obj.poolPair.idTokenA)) rpcInfo.pushKV("tokenA", tokenA->name); - if (auto tokenB = mnview.GetToken(obj.idTokenB)) + if (auto tokenB = mnview.GetToken(obj.poolPair.idTokenB)) rpcInfo.pushKV("tokenB", tokenB->name); - rpcInfo.pushKV("commission", ValueFromAmount(obj.commission)); - rpcInfo.pushKV("status", obj.status); - rpcInfo.pushKV("ownerAddress", ScriptToString(obj.ownerAddress)); + rpcInfo.pushKV("commission", ValueFromAmount(obj.poolPair.commission)); + rpcInfo.pushKV("status", obj.poolPair.status); + rpcInfo.pushKV("ownerAddress", ScriptToString(obj.poolPair.ownerAddress)); customRewardsInfo(obj.rewards); } @@ -251,8 +251,7 @@ class CCustomTxRpcVisitor { } void operator()(const CGovernanceMessage &obj) const { - for (const auto &gov : obj.govs) { - auto& var = gov.second; + for (const auto &var : obj.govs) { rpcInfo.pushKV(var->GetName(), var->Export()); } } diff --git a/src/masternodes/rpc_oracles.cpp b/src/masternodes/rpc_oracles.cpp index 95827037ac..0f49a4858e 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, strprintf("%s/%s is empty", oraclefields::Token, oraclefields::Currency)); + throw JSONRPCError(RPC_INVALID_PARAMETER, Res::Err("%s/%s is empty", oraclefields::Token, oraclefields::Currency).msg); } return std::make_pair(token, currency); @@ -853,8 +853,14 @@ ResVal GetAggregatePrice(CCustomCSView& view, const std::string& token, }); static const uint64_t minimumLiveOracles = Params().NetworkIDString() == CBaseChainParams::REGTEST ? 1 : 2; - Require(numLiveOracles >= minimumLiveOracles, "no live oracles for specified request"); - Require(sumWeights > 0, "all live oracles which meet specified request, have zero weight"); + + 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"); + } ResVal res((weightedSum / arith_uint256(sumWeights)).GetLow64(), Res::Ok()); diff --git a/src/masternodes/rpc_poolpair.cpp b/src/masternodes/rpc_poolpair.cpp index 5b735f6f88..d112ea4de0 100644 --- a/src/masternodes/rpc_poolpair.cpp +++ b/src/masternodes/rpc_poolpair.cpp @@ -660,22 +660,20 @@ UniValue createpoolpair(const JSONRPCRequest &request) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("pairSymbol is larger than %d", symbolLength)); } - CCreatePoolPairMessage poolPairMsg; + CPoolPairMessage poolPairMsg; poolPairMsg.idTokenA = idtokenA; poolPairMsg.idTokenB = idtokenB; poolPairMsg.commission = commission; poolPairMsg.status = status; poolPairMsg.ownerAddress = ownerAddress; - poolPairMsg.pairSymbol = pairSymbol; + + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); + metadata << static_cast(CustomTxType::CreatePoolPair) << poolPairMsg << pairSymbol; if (targetHeight >= Params().GetConsensus().ClarkeQuayHeight) { - poolPairMsg.rewards = rewards; + metadata << rewards; } - CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); - metadata << static_cast(CustomTxType::CreatePoolPair) - << poolPairMsg; - CScript scriptMeta; scriptMeta << OP_RETURN << ToByteVector(metadata); @@ -693,7 +691,6 @@ 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 e70a54bd0a..e8e4dc1dbb 100644 --- a/src/masternodes/tokens.cpp +++ b/src/masternodes/tokens.cpp @@ -23,18 +23,19 @@ 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 {}; } @@ -63,16 +64,20 @@ Res CTokensView::CreateDFIToken() { ResVal CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bool isPreBayfront) { // this should not happen, but for sure - Require(!GetTokenByCreationTx(token.creationTx), - "token with creation tx %s already exists!", - token.creationTx.ToString()); + if (GetTokenByCreationTx(token.creationTx)) { + return Res::Err("token with creation tx %s already exists!", token.creationTx.ToString()); + } - Require(token.IsValidSymbol()); + auto checkSymbolRes = token.IsValidSymbol(); + if (!checkSymbolRes.ok) { + return checkSymbolRes; + } DCT_ID id{0}; if (token.IsDAT()) { - Require(!GetToken(token.symbol), "token '%s' already exists!", token.symbol); - + if (GetToken(token.symbol)) { + return Res::Err("token '%s' already exists!", token.symbol); + } ForEachToken( [&](DCT_ID const ¤tId, CLazySerialize) { if (currentId < DCT_ID_START) @@ -81,15 +86,15 @@ ResVal CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bo }, id); if (id == DCT_ID_START) { - 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 - + 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 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); - Require(pair, "token with creationTx %s does not exist!", newToken.creationTx.ToString()); - + if (!pair) { + return Res::Err("token with creationTx %s does not exist!", newToken.creationTx.ToString()); + } DCT_ID id = pair->first; CTokenImpl &oldToken = pair->second; - 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"); + 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"); + } // 'name' and 'symbol' were trimmed in 'Apply' oldToken.name = newToken.name; // check new symbol correctness if (!tokenSplitUpdate) { - Require(newToken.IsValidSymbol()); + auto checkSymbolRes = newToken.IsValidSymbol(); + if (!checkSymbolRes.ok) { + return checkSymbolRes; + } } // deal with DB symbol indexes before touching symbols/DATs: @@ -132,8 +142,9 @@ 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); - Require(!GetToken(newSymbolKey), "token with key '%s' already exists!", newSymbolKey); - + if (GetToken(newSymbolKey)) { + return Res::Err("token with key '%s' already exists!", newSymbolKey); + } EraseBy(oldSymbolKey); WriteBy(newSymbolKey, id); } @@ -186,9 +197,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 @@ -197,11 +208,14 @@ Res CTokensView::BayfrontFlagsCleanup() { Res CTokensView::AddMintedTokens(DCT_ID const &id, const CAmount &amount) { auto tokenImpl = GetToken(id); - Require(tokenImpl, "token with id %d does not exist!", id.v); + if (!tokenImpl) { + return Res::Err("token with id %d does not exist!", id.v); + } auto resMinted = SafeAdd(tokenImpl->minted, amount); - Require(resMinted, "overflow when adding to minted"); - + if (!resMinted) { + return Res::Err("overflow when adding to minted"); + } tokenImpl->minted = resMinted; WriteBy(id, *tokenImpl); @@ -210,11 +224,14 @@ Res CTokensView::AddMintedTokens(DCT_ID const &id, const CAmount &amount) { Res CTokensView::SubMintedTokens(DCT_ID const &id, const CAmount &amount) { auto tokenImpl = GetToken(id); - Require(tokenImpl, "token with id %d does not exist!", id.v); + if (!tokenImpl) { + return Res::Err("token with id %d does not exist!", id.v); + } auto resMinted = tokenImpl->minted - amount; - Require(resMinted >= 0, "not enough tokens exist to subtract this amount"); - + if (resMinted < 0) { + return Res::Err("not enough tokens exist to subtract this amount"); + } tokenImpl->minted = resMinted; WriteBy(id, *tokenImpl); @@ -224,8 +241,9 @@ 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; } @@ -235,15 +253,18 @@ std::optional CTokensView::ReadLastDctId() const { if (Read(LastDctId::prefix(), lastDctId)) { return {lastDctId}; } - return {}; } inline Res CTokenImplementation::IsValidSymbol() const { - 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 '/'"); + 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 '/'"); } return Res::Ok(); } diff --git a/src/masternodes/tokens.h b/src/masternodes/tokens.h index 5bbc544f34..819d893c86 100644 --- a/src/masternodes/tokens.h +++ b/src/masternodes/tokens.h @@ -5,8 +5,9 @@ #ifndef DEFI_MASTERNODES_TOKENS_H #define DEFI_MASTERNODES_TOKENS_H -#include #include + +#include #include #include