diff --git a/src/Makefile.am b/src/Makefile.am index df16134615..725383e2ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -215,8 +215,9 @@ DEFI_CORE_H = \ rpc/register.h \ rpc/request.h \ rpc/server.h \ - rpc/util.h \ rpc/stats.h \ + rpc/resultcache.h \ + rpc/util.h \ scheduler.h \ script/descriptor.h \ script/keyorigin.h \ @@ -427,6 +428,7 @@ libdefi_server_a_SOURCES = \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ + rpc/resultcache.cpp \ script/sigcache.cpp \ shutdown.cpp \ spv/btctransaction.cpp \ @@ -603,6 +605,7 @@ libdefi_common_a_SOURCES = \ rpc/rawtransaction_util.cpp \ rpc/util.cpp \ rpc/stats.cpp \ + rpc/resultcache.cpp \ scheduler.cpp \ script/descriptor.cpp \ script/sign.cpp \ diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 7dc9ee2546..9fe0ce1e4d 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/src/init.cpp b/src/init.cpp index 0fc027bcd0..5c07b2a299 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -598,6 +599,7 @@ void SetupServerArgs() gArgs.AddArg("-rpcallowcors=", "Allow CORS requests from the given host origin. Include scheme and port (eg: -rpcallowcors=http://127.0.0.1:5000)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); gArgs.AddArg("-rpcstats", strprintf("Log RPC stats. (default: %u)", DEFAULT_RPC_STATS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); gArgs.AddArg("-consolidaterewards=", "Consolidate rewards on startup. Accepted multiple times for each token symbol", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-rpccache=<0/1/2>", "Cache rpc results - uses additional memory to hold on to the last results per block, but faster (0=none, 1=all, 2=smart)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); #if HAVE_DECL_DAEMON gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -815,6 +817,18 @@ static bool AppInitServers() { if (!gArgs.GetBoolArg("-rpcstats", DEFAULT_RPC_STATS)) statsRPC.setActive(false); + + auto rpcCacheModeVal = gArgs.GetArg("-rpccache", 1); + auto rpcCacheMode = [=](){ + switch (rpcCacheModeVal) { + case 1: return RPCResultCache::RPCCacheMode::All; + // For the moment, there is smart is dumb, just redirects to all. + // Future implementations could be smarter based on size / latency. + case 2: return RPCResultCache::RPCCacheMode::All; + default: return RPCResultCache::RPCCacheMode::None; + }}(); + GetRPCResultCache().Init(rpcCacheMode); + RPCServer::OnStarted(&OnRPCStarted); RPCServer::OnStopped(&OnRPCStopped); if (!InitHTTPServer()) diff --git a/src/logging.cpp b/src/logging.cpp index 2d3a68f2ab..bbf653a54a 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -156,7 +156,8 @@ const CLogCategoryDesc LogCategories[] = {BCLog::LOAN, "loan"}, {BCLog::ACCOUNTCHANGE, "accountchange"}, {BCLog::FUTURESWAP, "futureswap"}, - {BCLog::TOKEN_SPLIT, "tokensplit"}, + {BCLog::TOKENSPLIT, "tokensplit"}, + {BCLog::RPCCACHE, "rpccache"}, {BCLog::ALL, "1"}, {BCLog::ALL, "all"}, }; diff --git a/src/logging.h b/src/logging.h index 847565be64..7581b574ce 100644 --- a/src/logging.h +++ b/src/logging.h @@ -62,7 +62,9 @@ namespace BCLog { LOAN = (1 << 25), ACCOUNTCHANGE = (1 << 26), FUTURESWAP = (1 << 27), - TOKEN_SPLIT = (1 << 28), + TOKENSPLIT = (1 << 28), + RPCCACHE = (1 << 29), + // Note: We're almost hitting 32 bit threshold. ALL = ~(uint32_t)0, }; diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index 6c6c13e895..a6a4cf2c93 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -591,6 +592,7 @@ int CLastHeightView::GetLastHeight() const void CLastHeightView::SetLastHeight(int height) { + SetLastValidatedHeight(height); Write(Height::prefix(), height); } diff --git a/src/masternodes/mn_rpc.cpp b/src/masternodes/mn_rpc.cpp index 3a22d08459..521f0c3677 100644 --- a/src/masternodes/mn_rpc.cpp +++ b/src/masternodes/mn_rpc.cpp @@ -675,6 +675,7 @@ UniValue getgov(const JSONRPCRequest& request) { + HelpExampleRpc("getgov", "LP_DAILY_DFI_REWARD") }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; LOCK(cs_main); @@ -683,7 +684,7 @@ UniValue getgov(const JSONRPCRequest& request) { if (var) { UniValue ret(UniValue::VOBJ); ret.pushKV(var->GetName(),var->Export()); - return ret; + return GetRPCResultCache().Set(request, ret); } throw JSONRPCError(RPC_INVALID_REQUEST, "Variable '" + name + "' not registered"); } @@ -712,6 +713,8 @@ UniValue listgovs(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + GovVarsFilter mode{GovVarsFilter::All}; std::string prefix; if (request.params.size() > 0) { @@ -787,7 +790,7 @@ UniValue listgovs(const JSONRPCRequest& request) { result.push_back(innerResult); } - return result; + return GetRPCResultCache().Set(request, result); } diff --git a/src/masternodes/mn_rpc.h b/src/masternodes/mn_rpc.h index 2621e61880..7a2cb3f8fa 100644 --- a/src/masternodes/mn_rpc.h +++ b/src/masternodes/mn_rpc.h @@ -13,6 +13,7 @@ #include #include +#include #include //#ifdef ENABLE_WALLET diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index 4739d3c6f7..8144fc7f11 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -283,7 +283,7 @@ UniValue listaccounts(const JSONRPCRequest& request) { }, }.Check(request); - pwallet->BlockUntilSyncedToCurrentChain(); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; // parse pagination size_t limit = 100; @@ -350,7 +350,7 @@ UniValue listaccounts(const JSONRPCRequest& request) { return limit != 0; }, start.owner); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getaccount(const JSONRPCRequest& request) { @@ -382,6 +382,8 @@ UniValue getaccount(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + // decode owner const auto reqOwner = DecodeScript(request.params[0].get_str()); @@ -439,7 +441,7 @@ UniValue getaccount(const JSONRPCRequest& request) { limit--; return limit != 0; }, BalanceKey{reqOwner, start}); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue gettokenbalances(const JSONRPCRequest& request) { @@ -472,7 +474,7 @@ UniValue gettokenbalances(const JSONRPCRequest& request) { }, }.Check(request); - pwallet->BlockUntilSyncedToCurrentChain(); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; // parse pagination size_t limit = 100; @@ -540,7 +542,7 @@ UniValue gettokenbalances(const JSONRPCRequest& request) { else ret.push_back(ValueFromAmount(bal.nValue).getValStr() + "@" + tokenIdStr); } - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue utxostoaccount(const JSONRPCRequest& request) { @@ -987,6 +989,8 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is needed for account history"); } + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + uint32_t maxBlockHeight = std::numeric_limits::max(); uint32_t depth = maxBlockHeight; bool noRewards = false; @@ -1043,8 +1047,6 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { } } - pwallet->BlockUntilSyncedToCurrentChain(); - std::function isMatchOwner = [](CScript const &) { return true; }; @@ -1216,7 +1218,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { } } - return slice; + return GetRPCResultCache().Set(request, slice); } UniValue getaccounthistory(const JSONRPCRequest& request) { @@ -1244,6 +1246,8 @@ UniValue getaccounthistory(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is needed for account history"); } + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + auto owner = DecodeScript(request.params[0].getValStr()); uint32_t blockHeight = request.params[1].get_int(); uint32_t txn = request.params[2].get_int(); @@ -1256,7 +1260,7 @@ UniValue getaccounthistory(const JSONRPCRequest& request) { result = accounthistoryToJSON(AccountKey, *value); } - return result; + return GetRPCResultCache().Set(request, result); } UniValue listburnhistory(const JSONRPCRequest& request) { @@ -1289,6 +1293,8 @@ UniValue listburnhistory(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + uint32_t maxBlockHeight = std::numeric_limits::max(); uint32_t depth = maxBlockHeight; std::string tokenFilter; @@ -1337,8 +1343,6 @@ UniValue listburnhistory(const JSONRPCRequest& request) { } } - pwallet->BlockUntilSyncedToCurrentChain(); - std::function isMatchOwner = [](CScript const &) { return true; }; @@ -1411,7 +1415,7 @@ UniValue listburnhistory(const JSONRPCRequest& request) { } } - return slice; + return GetRPCResultCache().Set(request, slice); } UniValue accounthistorycount(const JSONRPCRequest& request) { @@ -1440,15 +1444,17 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { }, }.Check(request); + if (!paccountHistoryDB) { + throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is need for account history"); + } + + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + std::string accounts = "mine"; if (request.params.size() > 0) { accounts = request.params[0].getValStr(); } - if (!paccountHistoryDB) { - throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is need for account history"); - } - bool noRewards = false; std::string tokenFilter; auto txType = CustomTxType::None; @@ -1476,8 +1482,6 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { } } - pwallet->BlockUntilSyncedToCurrentChain(); - CScript owner; bool isMine = false; isminetype filter = ISMINE_ALL; @@ -1575,7 +1579,7 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { ); } - return count; + return GetRPCResultCache().Set(request, count); } UniValue listcommunitybalances(const JSONRPCRequest& request) { @@ -1592,6 +1596,7 @@ UniValue listcommunitybalances(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue ret(UniValue::VOBJ); LOCK(cs_main); @@ -1619,7 +1624,7 @@ UniValue listcommunitybalances(const JSONRPCRequest& request) { } ret.pushKV("Burnt", ValueFromAmount(burnt)); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue sendtokenstoaddress(const JSONRPCRequest& request) { @@ -1771,23 +1776,60 @@ UniValue getburninfo(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + CAmount burntDFI{0}; CAmount burntFee{0}; CAmount auctionFee{0}; CAmount paybackFee{0}; CAmount dfiPaybackFee{0}; + CAmount burnt{0}; + CBalances burntTokens; CBalances dexfeeburn; CBalances paybackfees; CBalances paybacktokens; CBalances dfi2203Tokens; + CBalances dfipaybacktokens; - UniValue dfipaybacktokens{UniValue::VARR}; + LOCK(cs_main); - auto calcBurn = [&](AccountHistoryKey const & key, CLazySerialize valueLazy) -> bool - { - const auto & value = valueLazy.get(); + auto height = ::ChainActive().Height(); + auto fortCanningHeight = Params().GetConsensus().FortCanningHeight; + auto burnAddress = Params().GetConsensus().burnAddress; + auto view = *pcustomcsview; + auto &burnView = pburnHistoryDB; + auto attributes = view.GetAttributes(); + + if (attributes) { + CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackDFITokens}; + auto tokenBalances = attributes->GetValue(liveKey, CBalances{}); + for (const auto& balance : tokenBalances.balances) { + if (balance.first == DCT_ID{0}) { + dfiPaybackFee = balance.second; + } else { + dfipaybacktokens.Add({balance.first, balance.second}); + } + } + liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackTokens}; + auto paybacks = attributes->GetValue(liveKey, CTokenPayback{}); + paybackfees = std::move(paybacks.tokensFee); + paybacktokens = std::move(paybacks.tokensPayback); + liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Burned}; + dfi2203Tokens = attributes->GetValue(liveKey, CBalances{}); + } + + for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) { + if (kv.first == CommunityAccountType::Unallocated || + kv.first == CommunityAccountType::IncentiveFunding || + (height >= fortCanningHeight && kv.first == CommunityAccountType::Loan)) { + burnt += view.GetCommunityBalance(kv.first); + } + } + + auto calculateBurnAmounts = [&](AccountHistoryKey const& key, CLazySerialize valueLazy) { + const auto & value = valueLazy.get(); // UTXO burn if (value.category == uint8_t(CustomTxType::None)) { for (auto const & diff : value.diff) { @@ -1795,7 +1837,6 @@ UniValue getburninfo(const JSONRPCRequest& request) { } return true; } - // Fee burn if (value.category == uint8_t(CustomTxType::CreateMasternode) || value.category == uint8_t(CustomTxType::CreateToken) @@ -1805,7 +1846,6 @@ UniValue getburninfo(const JSONRPCRequest& request) { } return true; } - // withdraw burn if (value.category == uint8_t(CustomTxType::PaybackLoan) || value.category == uint8_t(CustomTxType::PaybackLoanV2)) { @@ -1814,7 +1854,6 @@ UniValue getburninfo(const JSONRPCRequest& request) { } return true; } - // auction burn if (value.category == uint8_t(CustomTxType::AuctionBid)) { for (auto const & diff : value.diff) { @@ -1822,7 +1861,6 @@ UniValue getburninfo(const JSONRPCRequest& request) { } return true; } - // dex fee burn if (value.category == uint8_t(CustomTxType::PoolSwap) || value.category == uint8_t(CustomTxType::PoolSwapV2)) { @@ -1831,20 +1869,21 @@ UniValue getburninfo(const JSONRPCRequest& request) { } return true; } - // Token burn for (auto const & diff : value.diff) { burntTokens.Add({diff.first, diff.second}); } - return true; }; - AccountHistoryKey startKey{{}, std::numeric_limits::max(), std::numeric_limits::max()}; - pburnHistoryDB->ForEachAccountHistory(calcBurn, startKey); + AccountHistoryKey startKey{{}, + std::numeric_limits::max(), + std::numeric_limits::max()}; + + burnView->ForEachAccountHistory(calculateBurnAmounts, startKey); UniValue result(UniValue::VOBJ); - result.pushKV("address", ScriptToString(Params().GetConsensus().burnAddress)); + result.pushKV("address", ScriptToString(burnAddress)); result.pushKV("amount", ValueFromAmount(burntDFI)); result.pushKV("tokens", AmountsToJSON(burntTokens.balances)); @@ -1853,46 +1892,20 @@ UniValue getburninfo(const JSONRPCRequest& request) { result.pushKV("paybackburn", ValueFromAmount(paybackFee)); result.pushKV("dexfeetokens", AmountsToJSON(dexfeeburn.balances)); - LOCK(cs_main); - - if (auto attributes = pcustomcsview->GetAttributes()) { - CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackDFITokens}; - auto tokenBalances = attributes->GetValue(liveKey, CBalances{}); - for (const auto& balance : tokenBalances.balances) { - if (balance.first == DCT_ID{0}) { - dfiPaybackFee = balance.second; - } else { - dfipaybacktokens.push_back(tokenAmountString({balance.first, balance.second})); - } - } - liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::PaybackTokens}; - auto paybacks = attributes->GetValue(liveKey, CTokenPayback{}); - paybackfees = std::move(paybacks.tokensFee); - paybacktokens = std::move(paybacks.tokensPayback); - - liveKey = {AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Burned}; - dfi2203Tokens = attributes->GetValue(liveKey, CBalances{}); - } - result.pushKV("dfipaybackfee", ValueFromAmount(dfiPaybackFee)); - result.pushKV("dfipaybacktokens", dfipaybacktokens); + result.pushKV("dfipaybacktokens", AmountsToJSON(dfipaybacktokens.balances)); result.pushKV("paybackfees", AmountsToJSON(paybackfees.balances)); result.pushKV("paybacktokens", AmountsToJSON(paybacktokens.balances)); - CAmount burnt{0}; - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) { - if (kv.first == CommunityAccountType::Unallocated || kv.first == CommunityAccountType::IncentiveFunding || - (::ChainActive().Height() >= Params().GetConsensus().FortCanningHeight && kv.first == CommunityAccountType::Loan)) { - burnt += pcustomcsview->GetCommunityBalance(kv.first); - } - } result.pushKV("emissionburn", ValueFromAmount(burnt)); result.pushKV("dfip2203", AmountsToJSON(dfi2203Tokens.balances)); - return result; + return GetRPCResultCache() + .Set(request, result); } + UniValue HandleSendDFIP2201DFIInput(const JSONRPCRequest& request, CWalletCoinsUnlocker pwallet, const std::pair& contractPair, CTokenAmount amount) { CUtxosToAccountMessage msg{}; @@ -2232,6 +2245,7 @@ UniValue listpendingfutureswaps(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue listFutures{UniValue::VARR}; LOCK(cs_main); @@ -2267,7 +2281,7 @@ UniValue listpendingfutureswaps(const JSONRPCRequest& request) { return true; }); - return listFutures; + return GetRPCResultCache().Set(request, listFutures); } UniValue getpendingfutureswaps(const JSONRPCRequest& request) { @@ -2291,6 +2305,7 @@ UniValue getpendingfutureswaps(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue listValues{UniValue::VARR}; const auto owner = DecodeScript(request.params[0].get_str()); @@ -2333,7 +2348,7 @@ UniValue getpendingfutureswaps(const JSONRPCRequest& request) { UniValue obj{UniValue::VOBJ}; obj.pushKV("owner", request.params[0].get_str()); obj.pushKV("values", listValues); - return obj; + return GetRPCResultCache().Set(request, obj); } diff --git a/src/masternodes/rpc_loan.cpp b/src/masternodes/rpc_loan.cpp index 2ce9d64418..a74921aaae 100644 --- a/src/masternodes/rpc_loan.cpp +++ b/src/masternodes/rpc_loan.cpp @@ -1,9 +1,8 @@ #include - #include +#include extern UniValue tokenToJSON(CCustomCSView& view, DCT_ID const& id, CTokenImplementation const& token, bool verbose); -extern UniValue listauctions(const JSONRPCRequest& request); extern std::pair GetFixedIntervalPriceBlocks(int currentHeight, const CCustomCSView &mnview); UniValue setCollateralTokenToJSON(CCustomCSView& view, CLoanSetCollateralTokenImplementation const& collToken) @@ -196,6 +195,8 @@ UniValue getcollateraltoken(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters, arguments 1 must be non-null and expected as string for token symbol or id"); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + UniValue ret(UniValue::VOBJ); std::string tokenSymbol = request.params[0].get_str(); DCT_ID idToken; @@ -216,7 +217,7 @@ UniValue getcollateraltoken(const JSONRPCRequest& request) { ret.pushKVs(setCollateralTokenToJSON(*pcustomcsview, *collToken)); } - return (ret); + return GetRPCResultCache().Set(request, ret); } @@ -232,6 +233,7 @@ UniValue listcollateraltokens(const JSONRPCRequest& request) { HelpExampleCli("listcollateraltokens", "") }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue ret(UniValue::VARR); CCustomCSView view(*pcustomcsview); @@ -265,7 +267,7 @@ UniValue listcollateraltokens(const JSONRPCRequest& request) { return true; }, CDataStructureV0{AttributeTypes::Token}); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue setloantoken(const JSONRPCRequest& request) { @@ -511,6 +513,7 @@ UniValue listloantokens(const JSONRPCRequest& request) { HelpExampleCli("listloantokens", "") }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue ret(UniValue::VARR); @@ -543,7 +546,7 @@ UniValue listloantokens(const JSONRPCRequest& request) { return true; }, CDataStructureV0{AttributeTypes::Token}); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getloantoken(const JSONRPCRequest& request) @@ -560,13 +563,13 @@ UniValue getloantoken(const JSONRPCRequest& request) HelpExampleCli("getloantoken", "DFI")}, } .Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; RPCTypeCheck(request.params, {UniValue::VSTR}, false); if (request.params[0].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters, arguments 1 must be non-null and expected as string for token symbol or id"); - UniValue ret(UniValue::VOBJ); std::string tokenSymbol = request.params[0].get_str(); DCT_ID idToken; @@ -581,7 +584,9 @@ UniValue getloantoken(const JSONRPCRequest& request) throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("<%s> is not a valid loan token!", tokenSymbol.c_str())); } - return setLoanTokenToJSON(*pcustomcsview, *loanToken, idToken); + auto res = setLoanTokenToJSON(*pcustomcsview, *loanToken, idToken); + return GetRPCResultCache().Set(request, res); + } UniValue createloanscheme(const JSONRPCRequest& request) { @@ -925,6 +930,7 @@ UniValue listloanschemes(const JSONRPCRequest& request) { HelpExampleRpc("listloanschemes", "") }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; auto cmp = [](const CLoanScheme& a, const CLoanScheme& b) { return a.ratio == b.ratio ? a.rate < b.rate : a.ratio < b.ratio; @@ -958,7 +964,7 @@ UniValue listloanschemes(const JSONRPCRequest& request) { ret.push_back(arr); } - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getloanscheme(const JSONRPCRequest& request) { @@ -981,6 +987,8 @@ UniValue getloanscheme(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + if(request.params[0].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter id, argument must be non-null"); @@ -1006,7 +1014,7 @@ UniValue getloanscheme(const JSONRPCRequest& request) { result.pushKV("default", false); } - return result; + return GetRPCResultCache().Set(request, result); } UniValue takeloan(const JSONRPCRequest& request) { @@ -1312,67 +1320,130 @@ UniValue getloaninfo(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; UniValue ret{UniValue::VOBJ}; LOCK(cs_main); + auto view = *pcustomcsview; auto height = ::ChainActive().Height() + 1; bool useNextPrice = false, requireLivePrice = true; auto lastBlockTime = ::ChainActive().Tip()->GetBlockTime(); - uint64_t totalCollateralValue = 0, totalLoanValue = 0, totalVaults = 0, totalAuctions = 0; - - pcustomcsview->ForEachVault([&](const CVaultId& vaultId, const CVaultData& data) { - LogPrint(BCLog::LOAN,"getloaninfo()->Vault(%s):\n", vaultId.GetHex()); - auto collaterals = pcustomcsview->GetVaultCollaterals(vaultId); - if (!collaterals) - collaterals = CBalances{}; - auto rate = pcustomcsview->GetLoanCollaterals(vaultId, *collaterals, height, lastBlockTime, useNextPrice, requireLivePrice); - if (rate) - { - totalCollateralValue += rate.val->totalCollaterals; - totalLoanValue += rate.val->totalLoans; + + uint64_t totalCollateralValue = 0, totalLoanValue = 0, + totalVaults = 0, totalAuctions = 0, totalLoanSchemes = 0, + totalCollateralTokens = 0, totalLoanTokens = 0; + + auto fixedIntervalBlock = view.GetIntervalBlock(); + auto priceDeviation = view.GetPriceDeviation(); + auto defaultScheme = view.GetDefaultLoanScheme(); + auto priceBlocks = GetFixedIntervalPriceBlocks(::ChainActive().Height(), view); + + // TODO: Later optimize this into a general dynamic worker pool, so we don't + // need to recreate these threads on each call. + boost::asio::thread_pool workerPool{[]() { + const size_t workersMax = GetNumCores() - 1; + // More than 8 is likely not very fruitful for ~10k vaults. + return std::min(workersMax > 2 ? workersMax : 3, static_cast(8)); + }()}; + + boost::asio::post(workerPool, [&] { + view.ForEachLoanScheme([&](const std::string& identifier, const CLoanSchemeData& data) { + totalLoanSchemes++; + return true; + }); + + // First assume it's on the DB. For later, might be worth thinking if it's better to incorporate + // attributes right into the for each loop, so the interface remains consistent. + view.ForEachLoanCollateralToken([&](CollateralTokenKey const& key, uint256 const& collTokenTx) { + totalCollateralTokens++; + return true; + }); + + view.ForEachLoanToken([&](DCT_ID const& key, CLoanView::CLoanSetLoanTokenImpl loanToken) { + totalLoanTokens++; + return true; + }); + + // Now, let's go over attributes. If it's on attributes, the above calls would have done nothing. + auto attributes = view.GetAttributes(); + if (!attributes) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "attributes access failure"); } - totalVaults++; - return true; + + attributes->ForEach([&](const CDataStructureV0& attr, const CAttributeValue&) { + if (attr.type != AttributeTypes::Token) + return false; + if (attr.key == TokenKeys::LoanCollateralEnabled) + totalCollateralTokens++; + else if (attr.key == TokenKeys::LoanMintingEnabled) + totalLoanTokens++; + return true; + }, CDataStructureV0{AttributeTypes::Token}); + + view.ForEachVaultAuction([&](const CVaultId& vaultId, const CAuctionData& data) { + totalAuctions += data.batchCount; + return true; + }, height); }); - pcustomcsview->ForEachVaultAuction([&](const CVaultId& vaultId, const CAuctionData& data) { - totalAuctions += data.batchCount; + std::atomic vaultsTotal{0}; + std::atomic colsValTotal{0}; + std::atomic loansValTotal{0}; + + view.ForEachVault([&](const CVaultId& vaultId, const CVaultData& data) { + boost::asio::post(workerPool, [&, &colsValTotal=colsValTotal, + &loansValTotal=loansValTotal, &vaultsTotal=vaultsTotal, + vaultId=vaultId, height=height, useNextPrice=useNextPrice, + requireLivePrice=requireLivePrice] { + auto collaterals = view.GetVaultCollaterals(vaultId); + if (!collaterals) + collaterals = CBalances{}; + auto rate = view.GetLoanCollaterals(vaultId, *collaterals, height, lastBlockTime, useNextPrice, requireLivePrice); + if (rate) + { + colsValTotal.fetch_add(rate.val->totalCollaterals, std::memory_order_relaxed); + loansValTotal.fetch_add(rate.val->totalLoans, std::memory_order_relaxed); + } + vaultsTotal.fetch_add(1, std::memory_order_relaxed); + }); return true; - }, height); + }); + + workerPool.join(); + // We use relaxed ordering to increment. Thread joins should in theory, + // resolve have resulted in full barriers, but we ensure + // to throw in a full barrier anyway. x86 arch might appear to work without + // but let's be extra cautious about RISC optimizers. + totalVaults = vaultsTotal.load(); + totalLoanValue = loansValTotal.load(); + totalCollateralValue = colsValTotal.load(); UniValue totalsObj{UniValue::VOBJ}; - auto totalLoanSchemes = static_cast(listloanschemes(request).size()); - auto totalCollateralTokens = static_cast(listcollateraltokens(request).size()); totalsObj.pushKV("schemes", totalLoanSchemes); totalsObj.pushKV("collateralTokens", totalCollateralTokens); totalsObj.pushKV("collateralValue", ValueFromUint(totalCollateralValue)); - auto totalLoanTokens = static_cast(listloantokens(request).size()); totalsObj.pushKV("loanTokens", totalLoanTokens); totalsObj.pushKV("loanValue", ValueFromUint(totalLoanValue)); totalsObj.pushKV("openVaults", totalVaults); totalsObj.pushKV("openAuctions", totalAuctions); - UniValue defaultsObj{UniValue::VOBJ}; - auto defaultScheme = pcustomcsview->GetDefaultLoanScheme(); if(!defaultScheme) defaultsObj.pushKV("scheme", ""); else defaultsObj.pushKV("scheme", *defaultScheme); - defaultsObj.pushKV("maxPriceDeviationPct", ValueFromUint(pcustomcsview->GetPriceDeviation() * 100)); + defaultsObj.pushKV("maxPriceDeviationPct", ValueFromUint(priceDeviation * 100)); auto minLiveOracles = Params().NetworkIDString() == CBaseChainParams::REGTEST ? 1 : 2; defaultsObj.pushKV("minOraclesPerPrice", minLiveOracles); - defaultsObj.pushKV("fixedIntervalBlocks", int(pcustomcsview->GetIntervalBlock())); - - auto priceBlocks = GetFixedIntervalPriceBlocks(::ChainActive().Height(), *pcustomcsview); + defaultsObj.pushKV("fixedIntervalBlocks", int(fixedIntervalBlock)); ret.pushKV("currentPriceBlock", (int)priceBlocks.first); ret.pushKV("nextPriceBlock", (int)priceBlocks.second); ret.pushKV("defaults", defaultsObj); ret.pushKV("totals", totalsObj); - return (ret); + return GetRPCResultCache().Set(request, ret); } UniValue getinterest(const JSONRPCRequest& request) { @@ -1398,6 +1469,8 @@ UniValue getinterest(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}, false); auto loanSchemeId = request.params[0].get_str(); @@ -1466,7 +1539,7 @@ UniValue getinterest(const JSONRPCRequest& request) { } ret.push_back(obj); } - return ret; + return GetRPCResultCache().Set(request, ret); } diff --git a/src/masternodes/rpc_masternodes.cpp b/src/masternodes/rpc_masternodes.cpp index 12d8326e8f..bb9af22969 100644 --- a/src/masternodes/rpc_masternodes.cpp +++ b/src/masternodes/rpc_masternodes.cpp @@ -638,6 +638,8 @@ UniValue listmasternodes(const JSONRPCRequest& request) }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = true; if (request.params.size() > 1) { verbose = request.params[1].get_bool(); @@ -680,13 +682,11 @@ UniValue listmasternodes(const JSONRPCRequest& request) return limit != 0; }, start); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getmasternode(const JSONRPCRequest& request) { - auto pwallet = GetWallet(request); - RPCHelpMan{"getmasternode", "\nReturns information about specified masternode.\n", { @@ -701,13 +701,17 @@ UniValue getmasternode(const JSONRPCRequest& request) }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + auto pwallet = GetWallet(request); + uint256 id = ParseHashV(request.params[0], "masternode id"); LOCK(cs_main); const auto mnIds = pcustomcsview->GetOperatorsMulti(); auto node = pcustomcsview->GetMasternode(id); if (node) { - return mnToJSON(id, *node, true, mnIds, pwallet); // or maybe just node, w/o id? + auto res = mnToJSON(id, *node, true, mnIds, pwallet); // or maybe just node, w/o id? + return GetRPCResultCache().Set(request, res); } throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); } @@ -734,6 +738,8 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + UniValue identifier = request.params[0].get_obj(); int idCount{0}; uint256 mn_id; @@ -846,7 +852,7 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { } } - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getanchorteams(const JSONRPCRequest& request) @@ -865,6 +871,8 @@ UniValue getanchorteams(const JSONRPCRequest& request) }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + int blockHeight; LOCK(cs_main); @@ -910,7 +918,7 @@ UniValue getanchorteams(const JSONRPCRequest& request) result.pushKV("auth", authRes); result.pushKV("confirm", confirmRes); - return result; + return GetRPCResultCache().Set(request, result); } @@ -929,6 +937,7 @@ UniValue getactivemasternodecount(const JSONRPCRequest& request) + HelpExampleRpc("getactivemasternodecount", "20160") }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; int blockSample{7 * 2880}; // One week if (!request.params[0].isNull()) { @@ -946,7 +955,8 @@ UniValue getactivemasternodecount(const JSONRPCRequest& request) } } - return static_cast(masternodes.size()); + auto res = static_cast(masternodes.size()); + return GetRPCResultCache().Set(request, res); } UniValue listanchors(const JSONRPCRequest& request) @@ -964,6 +974,8 @@ UniValue listanchors(const JSONRPCRequest& request) }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + LOCK(cs_main); auto confirms = pcustomcsview->CAnchorConfirmsView::GetAnchorConfirmData(); @@ -988,7 +1000,7 @@ UniValue listanchors(const JSONRPCRequest& request) result.push_back(entry); } - return result; + return GetRPCResultCache().Set(request, result); } static const CRPCCommand commands[] = diff --git a/src/masternodes/rpc_oracles.cpp b/src/masternodes/rpc_oracles.cpp index b2e695dcdc..34bef72b72 100644 --- a/src/masternodes/rpc_oracles.cpp +++ b/src/masternodes/rpc_oracles.cpp @@ -603,7 +603,7 @@ UniValue getoracledata(const JSONRPCRequest &request) { }.Check(request); RPCTypeCheck(request.params, {UniValue::VSTR}, false); - + if (auto res = GetRPCResultCache().TryGet(request)) return *res; // decode oracle id COracleId oracleId = ParseHashV(request.params[0], "oracleid"); @@ -615,7 +615,8 @@ UniValue getoracledata(const JSONRPCRequest &request) { throw JSONRPCError(RPC_DATABASE_ERROR, oracleRes.msg); } - return OracleToJSON(oracleId, *oracleRes.val); + auto res = OracleToJSON(oracleId, *oracleRes.val); + return GetRPCResultCache().Set(request, res); } UniValue listoracles(const JSONRPCRequest &request) { @@ -655,6 +656,8 @@ UniValue listoracles(const JSONRPCRequest &request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + // parse pagination COracleId start = {}; bool including_start = true; @@ -679,7 +682,7 @@ UniValue listoracles(const JSONRPCRequest &request) { LOCK(cs_main); - UniValue value(UniValue::VARR); + UniValue res(UniValue::VARR); CCustomCSView view(*pcustomcsview); view.ForEachOracle([&](const COracleId& id, CLazySerialize) { if (!including_start) @@ -687,12 +690,12 @@ UniValue listoracles(const JSONRPCRequest &request) { including_start = true; return (true); } - value.push_back(id.GetHex()); + res.push_back(id.GetHex()); limit--; return limit != 0; }, start); - return value; + return GetRPCResultCache().Set(request, res); } UniValue listlatestrawprices(const JSONRPCRequest &request) { @@ -738,6 +741,7 @@ UniValue listlatestrawprices(const JSONRPCRequest &request) { }.Check(request); RPCTypeCheck(request.params, {UniValue::VOBJ}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; std::optional tokenPair; @@ -810,7 +814,7 @@ UniValue listlatestrawprices(const JSONRPCRequest &request) { } return limit != 0; }, start); - return result; + return GetRPCResultCache().Set(request, result); } ResVal GetAggregatePrice(CCustomCSView& view, const std::string& token, const std::string& currency, uint64_t lastBlockTime) { @@ -945,6 +949,7 @@ UniValue getprice(const JSONRPCRequest &request) { }.Check(request); RPCTypeCheck(request.params, {UniValue::VOBJ}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; auto tokenPair = DecodeTokenCurrencyPair(request.params[0]); @@ -954,7 +959,8 @@ UniValue getprice(const JSONRPCRequest &request) { auto result = GetAggregatePrice(view, tokenPair.first, tokenPair.second, lastBlockTime); if (!result) throw JSONRPCError(RPC_MISC_ERROR, result.msg); - return ValueFromAmount(*result.val); + auto res = ValueFromAmount(*result.val); + return GetRPCResultCache().Set(request, res); } UniValue listprices(const JSONRPCRequest& request) { @@ -1001,6 +1007,7 @@ UniValue listprices(const JSONRPCRequest& request) { }.Check(request); RPCTypeCheck(request.params, {}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; // parse pagination UniValue paginationObj(UniValue::VOBJ); @@ -1011,7 +1018,8 @@ UniValue listprices(const JSONRPCRequest& request) { LOCK(cs_main); CCustomCSView view(*pcustomcsview); auto lastBlockTime = ::ChainActive().Tip()->GetBlockTime(); - return GetAllAggregatePrices(view, lastBlockTime, paginationObj); + auto res = GetAllAggregatePrices(view, lastBlockTime, paginationObj); + return GetRPCResultCache().Set(request, res); } UniValue getfixedintervalprice(const JSONRPCRequest& request) { @@ -1037,6 +1045,8 @@ UniValue getfixedintervalprice(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + auto fixedIntervalStr = request.params[0].getValStr(); UniValue objPrice{UniValue::VOBJ}; objPrice.pushKV("fixedIntervalPriceId", fixedIntervalStr); @@ -1056,7 +1066,7 @@ UniValue getfixedintervalprice(const JSONRPCRequest& request) { objPrice.pushKV("nextPriceBlock", (int)priceBlocks.second); objPrice.pushKV("timestamp", fixedPrice.val->timestamp); objPrice.pushKV("isLive", fixedPrice.val->isLive(pcustomcsview->GetPriceDeviation())); - return objPrice; + return GetRPCResultCache().Set(request, objPrice); } UniValue listfixedintervalprices(const JSONRPCRequest& request) { @@ -1088,6 +1098,8 @@ UniValue listfixedintervalprices(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + size_t limit = 100; CTokenCurrencyPair start{}; { @@ -1121,7 +1133,7 @@ UniValue listfixedintervalprices(const JSONRPCRequest& request) { limit--; return limit != 0; }, start); - return listPrice; + return GetRPCResultCache().Set(request, listPrice); } UniValue getfutureswapblock(const JSONRPCRequest& request) { @@ -1136,6 +1148,7 @@ UniValue getfutureswapblock(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; LOCK(cs_main); const auto currentHeight = ::ChainActive().Height(); @@ -1145,7 +1158,8 @@ UniValue getfutureswapblock(const JSONRPCRequest& request) { return 0; } - return currentHeight + (*block - (currentHeight % *block)); + auto res = currentHeight + (*block - (currentHeight % *block)); + return GetRPCResultCache().Set(request, res); } diff --git a/src/masternodes/rpc_poolpair.cpp b/src/masternodes/rpc_poolpair.cpp index 34f76b55cc..b43ade3da5 100644 --- a/src/masternodes/rpc_poolpair.cpp +++ b/src/masternodes/rpc_poolpair.cpp @@ -179,6 +179,8 @@ UniValue listpoolpairs(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = true; if (request.params.size() > 1) { verbose = request.params[1].get_bool(); @@ -223,7 +225,7 @@ UniValue listpoolpairs(const JSONRPCRequest& request) { return limit != 0; }, start); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue getpoolpair(const JSONRPCRequest& request) { @@ -244,6 +246,8 @@ UniValue getpoolpair(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = true; if (request.params.size() > 1) { verbose = request.params[1].getBool(); @@ -256,7 +260,8 @@ UniValue getpoolpair(const JSONRPCRequest& request) { if (token) { auto pool = pcustomcsview->GetPoolPair(id); if (pool) { - return poolToJSON(id, *pool, *token, verbose); + auto res = poolToJSON(id, *pool, *token, verbose); + return GetRPCResultCache().Set(request, res); } throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pool not found"); } @@ -1181,6 +1186,8 @@ UniValue listpoolshares(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = true; if (request.params.size() > 1) { verbose = request.params[1].getBool(); @@ -1246,7 +1253,7 @@ UniValue listpoolshares(const JSONRPCRequest& request) { return limit != 0; }, startKey); - return ret; + return GetRPCResultCache().Set(request, ret); } static const CRPCCommand commands[] = diff --git a/src/masternodes/rpc_tokens.cpp b/src/masternodes/rpc_tokens.cpp index ea55e3f4bb..4218f2c14e 100644 --- a/src/masternodes/rpc_tokens.cpp +++ b/src/masternodes/rpc_tokens.cpp @@ -390,6 +390,8 @@ UniValue listtokens(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = true; if (request.params.size() > 1) { verbose = request.params[1].get_bool(); @@ -431,7 +433,7 @@ UniValue listtokens(const JSONRPCRequest& request) { return limit != 0; }, start); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue gettoken(const JSONRPCRequest& request) { @@ -450,12 +452,15 @@ UniValue gettoken(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + LOCK(cs_main); DCT_ID id; auto token = pcustomcsview->GetTokenGuessId(request.params[0].getValStr(), id); if (token) { - return tokenToJSON(*pcustomcsview, id, *token, true); + auto res = tokenToJSON(*pcustomcsview, id, *token, true); + return GetRPCResultCache().Set(request, res); } throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Token not found"); } diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index a6d5a8d1c8..e186f25e39 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -473,6 +473,8 @@ UniValue listvaults(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + CScript ownerAddress = {}; std::string loanSchemeId; VaultState state{VaultState::Unknown}; @@ -546,7 +548,7 @@ UniValue listvaults(const JSONRPCRequest& request) { return limit != 0; }, start, ownerAddress); - return valueArr; + return GetRPCResultCache().Set(request, valueArr); } UniValue getvault(const JSONRPCRequest& request) { @@ -567,6 +569,8 @@ UniValue getvault(const JSONRPCRequest& request) { }.Check(request); RPCTypeCheck(request.params, {UniValue::VSTR}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + bool verbose = request.params[1].getBool(); CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); @@ -577,7 +581,8 @@ UniValue getvault(const JSONRPCRequest& request) { throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("Vault <%s> not found", vaultId.GetHex())); } - return VaultToJSON(vaultId, *vault, verbose); + auto res = VaultToJSON(vaultId, *vault, verbose); + return GetRPCResultCache().Set(request, res); } UniValue updatevault(const JSONRPCRequest& request) { @@ -1046,6 +1051,8 @@ UniValue listauctions(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + // parse pagination CVaultId vaultId; size_t limit = 100; @@ -1089,7 +1096,7 @@ UniValue listauctions(const JSONRPCRequest& request) { return --limit != 0; }, height, vaultId); - return valueArr; + return GetRPCResultCache().Set(request, valueArr); } UniValue auctionhistoryToJSON(AuctionHistoryKey const & key, AuctionHistoryValue const & value) { @@ -1150,7 +1157,7 @@ UniValue listauctionhistory(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_REQUEST, "-acindex is needed for auction history"); } - pwallet->BlockUntilSyncedToCurrentChain(); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; // parse pagination size_t limit = 100; @@ -1215,7 +1222,7 @@ UniValue listauctionhistory(const JSONRPCRequest& request) { return --limit != 0; }, start); - return ret; + return GetRPCResultCache().Set(request, ret); } UniValue vaultToJSON(const uint256& vaultID, const std::string& address, const uint64_t blockHeight, const std::string& type, @@ -1329,6 +1336,8 @@ UniValue listvaulthistory(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_REQUEST, "-vaultindex required for vault history"); } + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + uint256 vaultID = ParseHashV(request.params[0], "vaultId"); uint32_t maxBlockHeight = std::numeric_limits::max(); uint32_t depth = maxBlockHeight; @@ -1378,8 +1387,6 @@ UniValue listvaulthistory(const JSONRPCRequest& request) { } } - pwallet->BlockUntilSyncedToCurrentChain(); - std::function isMatchVault = [&vaultID](uint256 const & id) { return id == vaultID; }; @@ -1559,7 +1566,7 @@ UniValue listvaulthistory(const JSONRPCRequest& request) { } } - return slice; + return GetRPCResultCache().Set(request, slice); } UniValue estimateloan(const JSONRPCRequest& request) { @@ -1587,6 +1594,8 @@ UniValue estimateloan(const JSONRPCRequest& request) { RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VNUM}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); LOCK(cs_main); @@ -1655,7 +1664,8 @@ UniValue estimateloan(const JSONRPCRequest& request) { if (totalSplit != COIN) throw JSONRPCError(RPC_MISC_ERROR, strprintf("total split between loan tokens = %s vs expected %s", GetDecimaleString(totalSplit), GetDecimaleString(COIN))); } - return AmountsToJSON(loanBalances.balances); + auto res = AmountsToJSON(loanBalances.balances); + return GetRPCResultCache().Set(request, res); } UniValue estimatecollateral(const JSONRPCRequest& request) { @@ -1684,6 +1694,7 @@ UniValue estimatecollateral(const JSONRPCRequest& request) { }.Check(request); RPCTypeCheck(request.params, {UniValueType(), UniValue::VNUM, UniValue::VOBJ}, false); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; const CBalances loanBalances = DecodeAmounts(pwallet->chain(), request.params[0], ""); auto ratio = request.params[1].get_int(); @@ -1747,7 +1758,8 @@ UniValue estimatecollateral(const JSONRPCRequest& request) { throw JSONRPCError(RPC_MISC_ERROR, strprintf("total split between collateral tokens = %s vs expected %s", GetDecimaleString(totalSplit), GetDecimaleString(COIN))); } - return AmountsToJSON(collateralBalances.balances); + auto res = AmountsToJSON(collateralBalances.balances); + return GetRPCResultCache().Set(request, res); } UniValue estimatevault(const JSONRPCRequest& request) { @@ -1777,6 +1789,8 @@ UniValue estimatevault(const JSONRPCRequest& request) { }, }.Check(request); + if (auto res = GetRPCResultCache().TryGet(request)) return *res; + CBalances collateralBalances = DecodeAmounts(pwallet->chain(), request.params[0], ""); CBalances loanBalances = DecodeAmounts(pwallet->chain(), request.params[1], ""); @@ -1814,7 +1828,7 @@ UniValue estimatevault(const JSONRPCRequest& request) { ret.pushKV("loanValue", ValueFromUint(result.totalLoans)); ret.pushKV("informativeRatio", ValueFromAmount(result.precisionRatio())); ret.pushKV("collateralRatio", int(result.ratio())); - return ret; + return GetRPCResultCache().Set(request, ret); } static const CRPCCommand commands[] = diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 0889e6d161..a2ff3f4072 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -546,8 +546,6 @@ static UniValue getversioninfo(const JSONRPCRequest& request){ UniValue nodeInfoObj(UniValue::VOBJ); - - UniValue btcInfoObj(UniValue::VOBJ); btcInfoObj.pushKV("version", BR_PROTOCOL_VERSION); btcInfoObj.pushKV("min", BR_MIN_PROTO_VERSION); diff --git a/src/rpc/resultcache.cpp b/src/rpc/resultcache.cpp new file mode 100644 index 0000000000..c6c90e1f30 --- /dev/null +++ b/src/rpc/resultcache.cpp @@ -0,0 +1,81 @@ +// +// Created by pvl on 4/6/22. +// + +#include +#include +#include + +void RPCResultCache::Init(RPCCacheMode mode) { + CLockFreeGuard lock{syncFlag}; + this->mode = mode; +} + +std::string GetKey(const JSONRPCRequest &request) { + std::stringstream ss; + ss << request.strMethod << '/' << request.authUser << '/' << request.params.write(); + return ss.str(); +} + +bool RPCResultCache::InvalidateCaches() { + CLockFreeGuard lock{syncFlag}; + auto height = GetLastValidatedHeight(); + if (cacheHeight != height) { + LogPrint(BCLog::RPCCACHE, "RPCCache: clear\n"); + if (cacheMap.size()) cacheMap.clear(); + cacheHeight = height; + return true; + } + return false; +} + +std::optional RPCResultCache::TryGet(const JSONRPCRequest &request) { + auto cacheMode = mode; + if (cacheMode == RPCCacheMode::None) return {}; + if (cacheMode == RPCCacheMode::Smart && + smartModeList.find(request.strMethod) == smartModeList.end()) return {}; + auto key = GetKey(request); + UniValue val; + { + CLockFreeGuard lock{syncFlag}; + if (auto res = cacheMap.find(key); res != cacheMap.end()) { + if (LogAcceptCategory(BCLog::RPCCACHE)) { + LogPrint(BCLog::RPCCACHE, "RPCCache: hit: key: %d/%s, val: %s\n", cacheHeight, key, res->second.write()); + } + return res->second; + } + } + return {}; +} + +const UniValue& RPCResultCache::Set(const JSONRPCRequest &request, const UniValue &value) { + auto key = GetKey(request); + { + CLockFreeGuard lock{syncFlag}; + if (LogAcceptCategory(BCLog::RPCCACHE)) { + LogPrint(BCLog::RPCCACHE, "RPCCache: set: key: %d/%s, val: %s\n", cacheHeight, key, value.write()); + } + cacheMap[key] = value; + } + return value; +} + +// Note: We initialize all the globals in the init phase. So, it's safe. Otherwise, +// static init is undefined behavior when multiple threads init them at the same time. +RPCResultCache& GetRPCResultCache() { + static RPCResultCache g_rpcResultCache; + return g_rpcResultCache; +} + +static std::atomic g_lastValidatedHeight{0}; + +int GetLastValidatedHeight() { + auto res = g_lastValidatedHeight.load(std::memory_order_acquire); + return res; +} + +void SetLastValidatedHeight(int height) { + LogPrint(BCLog::RPCCACHE, "RPCCache: set height: %d\n", height); + g_lastValidatedHeight.store(height, std::memory_order_release); + GetRPCResultCache().InvalidateCaches(); +} diff --git a/src/rpc/resultcache.h b/src/rpc/resultcache.h new file mode 100644 index 0000000000..6431583d79 --- /dev/null +++ b/src/rpc/resultcache.h @@ -0,0 +1,44 @@ +// +// Created by pvl on 4/6/22. +// + +#ifndef DEFI_RPC_RESULTCACHE_H +#define DEFI_RPC_RESULTCACHE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class RPCResultCache { +public: + enum RPCCacheMode { + None, + Smart, + All + }; + + void Init(RPCCacheMode mode); + std::optional TryGet(const JSONRPCRequest &request); + const UniValue& Set(const JSONRPCRequest &request, const UniValue &value); + bool InvalidateCaches(); + +private: + std::atomic_bool syncFlag{false}; + std::set smartModeList{}; + RPCCacheMode mode{RPCCacheMode::None}; + std::map cacheMap{}; + int cacheHeight{0}; + +}; + +RPCResultCache& GetRPCResultCache(); + +int GetLastValidatedHeight(); +void SetLastValidatedHeight(int height); + +#endif //DEFI_RPC_RESULTCACHE_H diff --git a/src/validation.cpp b/src/validation.cpp index cba13ccdbf..a68d55ece4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4119,7 +4119,7 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at auto oldPoolLogStr = CTokenAmount{oldPoolId, amount}.ToString(); auto newPoolLogStr = CTokenAmount{newPoolId, liquidity}.ToString(); - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: LP (%s: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: LP (%s: %s => %s)\n", ScriptToString(owner), oldPoolLogStr, newPoolLogStr); view.SetShare(newPoolId, owner, pindex->nHeight); @@ -4248,7 +4248,7 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID auto oldTokenAmount = CTokenAmount{oldTokenId, amount}; auto newTokenAmount = CTokenAmount{newTokenId, newAmount}; - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: V Loan (%s: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V Loan (%s: %s => %s)\n", vaultId.ToString(), oldTokenAmount.ToString(), newTokenAmount.ToString()); res = view.AddLoanToken(vaultId, newTokenAmount); @@ -4292,8 +4292,8 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID rate.interestPerBlock = newInterestRatePerBlock; } - if (LogAcceptCategory(BCLog::TOKEN_SPLIT)) { - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: V Interest (%s: %s => %s, %s => %s)\n", + if (LogAcceptCategory(BCLog::TOKENSPLIT)) { + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V Interest (%s: %s => %s, %s => %s)\n", vaultId.ToString(), GetInterestPerBlockHighPrecisionString(oldRateToHeight), GetInterestPerBlockHighPrecisionString(newRateToHeight), @@ -4326,7 +4326,7 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID auto newLoanInterest = CalculateNewAmount(multiplier, value.loanInterest); value.loanInterest = newLoanInterest; - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: V AuctionL (%s,%d: %s => %s, %d => %d)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V AuctionL (%s,%d: %s => %s, %d => %d)\n", key.first.ToString(), key.second, oldLoanAmount.ToString(), newLoanAmount.ToString(), oldInterest, newLoanInterest); } @@ -4338,7 +4338,7 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID value.collaterals.balances[newAmount.nTokenId] = newAmount.nValue; value.collaterals.balances.erase(oldAmount.nTokenId); - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: V AuctionC (%s,%d: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V AuctionC (%s,%d: %s => %s)\n", key.first.ToString(), key.second, oldAmount.ToString(), newAmount.ToString()); } @@ -4365,7 +4365,7 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID view.StoreAuctionBid(key, value); - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: V Bid (%s,%d: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V Bid (%s,%d: %s => %s)\n", key.first.ToString(), key.second, oldTokenAmount.ToString(), newTokenAmount.ToString()); } @@ -4507,7 +4507,7 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin totalBalance += newBalance; auto newBalanceStr = CTokenAmount{newTokenId, newBalance}.ToString(); - LogPrint(BCLog::TOKEN_SPLIT, "TokenSplit: T (%s: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: T (%s: %s => %s)\n", ScriptToString(owner), balance.ToString(), newBalanceStr); }