From abc7a951ef2090d436de6d6ba4690653649af942 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Sat, 14 Dec 2024 09:04:58 +0400 Subject: [PATCH] Implement new rpc calls to get anonymity sets --- src/rpc/misc.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++++ src/rpc/server.cpp | 2 + src/rpc/server.h | 2 + src/spark/state.cpp | 79 +++++++++++++++++++++++++ src/spark/state.h | 15 +++++ 5 files changed, 235 insertions(+) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 59705edb9f..59c0680a12 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1307,6 +1307,141 @@ UniValue getsparkanonymityset(const JSONRPCRequest& request) return ret; } +UniValue getsparkanonymitysetmeta(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "getsparkanonymitysetmeta\n" + "\nReturns the anonymity set and latest block hash.\n" + "\nArguments:\n" + "{\n" + " \"coinGroupId\" (int)\n" + "}\n" + "\nResult:\n" + "{\n" + " \"blockHash\" (string) Latest block hash for anonymity set\n" + " \"setHash\" (string) Anonymity set hash\n" + " \"size\" (int) set size\n" + "}\n" + + HelpExampleCli("getsparkanonymitysetmeta", "\"1\" ") + + HelpExampleRpc("getsparkanonymitysetmeta", "\"1\" ") + ); + + + int coinGroupId; + try { + coinGroupId = std::stol(request.params[0].get_str()); + } catch (std::logic_error const & e) { + throw std::runtime_error(std::string("An exception occurred while parsing parameters: ") + e.what()); + } + + if(!GetBoolArg("-mobile", false)){ + throw std::runtime_error(std::string("Please rerun Firo with -mobile ")); + } + + uint256 blockHash; + std::vector setHash; + int size; + { + LOCK(cs_main); + spark::CSparkState* sparkState = spark::CSparkState::GetState(); + sparkState->GetAnonSetMetaData( + &chainActive, + chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), + coinGroupId, + blockHash, + setHash, + size); + } + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("blockHash", EncodeBase64(blockHash.begin(), blockHash.size()))); + ret.push_back(Pair("setHash", UniValue(EncodeBase64(setHash.data(), setHash.size())))); + ret.push_back(Pair("size", size)); + + return ret; +} + +UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 4) + throw std::runtime_error( + "getsparkanonymitysetsector\n" + "\nReturns the anonymity sector based on provided data.\n" + "\nArguments:\n" + "{\n" + " \"coinGroupId\" (int)\n" + " \"latestBlock\" (string)\n" + " \"startIndex\" (int)\n" + " \"endIndex\" (int)\n" + "}\n" + "\nResult:\n" + "{\n" + " \"mints\" (Pair) Serialized Spark coin paired with txhash\n" + "}\n" + + HelpExampleCli("getsparkanonymitysetsector", "\"1\" " "\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"" "\"0\" " "\"1000\" ") + + HelpExampleRpc("getsparkanonymitysetsector", "\"1\" " "\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"" "\"0\" " "\"1000\" ") + ); + + + int coinGroupId; + std::string latestBlock; + int startIndex; + int endIndex; + + try { + coinGroupId = std::stol(request.params[0].get_str()); + latestBlock = request.params[1].get_str(); + startIndex = std::stol(request.params[2].get_str()); + endIndex = std::stol(request.params[3].get_str()); + + } catch (std::logic_error const & e) { + throw std::runtime_error(std::string("An exception occurred while parsing parameters: ") + e.what()); + } + + if(!GetBoolArg("-mobile", false)) { + throw std::runtime_error(std::string("Please rerun Firo with -mobile ")); + } + std::vector>>> coins; + uint256 blockHash = uint256S(latestBlock); + { + LOCK(cs_main); + spark::CSparkState* sparkState = spark::CSparkState::GetState(); + sparkState->GetCoinsForRecovery( + &chainActive, + chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), + coinGroupId, + blockHash, + coins); + } + + UniValue ret(UniValue::VOBJ); + UniValue mints(UniValue::VARR); + + std::size_t counter = 0; + for (const auto& coin : coins) { + if (counter < startIndex || ++counter > endIndex) + continue; + + CDataStream serializedCoin(SER_NETWORK, PROTOCOL_VERSION); + serializedCoin << coin; + std::vector vch(serializedCoin.begin(), serializedCoin.end()); + + std::vector data; + data.push_back(EncodeBase64(vch.data(), size_t(vch.size()))); // coin + data.push_back(EncodeBase64(coin.second.first.begin(), coin.second.first.size())); // tx hash + data.push_back(EncodeBase64(coin.second.second.data(), coin.second.second.size())); // spark serial context + + UniValue entity(UniValue::VARR); + entity.push_backV(data); + mints.push_back(entity); + } + + ret.push_back(Pair("coins", mints)); + + return ret; +} + UniValue getsparkmintmetadata(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) @@ -1921,6 +2056,8 @@ static const CRPCCommand commands[] = /* Mobile Spark */ { "mobile", "getsparkanonymityset", &getsparkanonymityset, false }, + { "mobile", "getsparkanonymitysetmeta", &getsparkanonymitysetmeta, false }, + { "mobile", "getsparkanonymitysetsector", &getsparkanonymitysetsector, false }, { "mobile", "getsparkmintmetadata", &getsparkmintmetadata, true }, { "mobile", "getusedcoinstags", &getusedcoinstags, false }, { "mobile", "getusedcoinstagstxhashes", &getusedcoinstagstxhashes, false }, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 0f07787f1b..f9af051bad 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -340,6 +340,8 @@ static const CRPCCommand vRPCCommands[] = /* Mobile Spark */ { "mobile", "getsparkanonymityset", &getsparkanonymityset, false }, + { "mobile", "getsparkanonymitysetmeta", &getsparkanonymitysetmeta, false }, + { "mobile", "getsparkanonymitysetsector", &getsparkanonymitysetsector, false }, { "mobile", "getsparkmintmetadata", &getsparkmintmetadata, true }, { "mobile", "getusedcoinstags", &getusedcoinstags, false }, { "mobile", "getusedcoinstagstxhashes", &getusedcoinstagstxhashes, false }, diff --git a/src/rpc/server.h b/src/rpc/server.h index dbf98839b9..f19dba9325 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -214,6 +214,8 @@ extern UniValue getfeerate(const JSONRPCRequest& params); extern UniValue getlatestcoinid(const JSONRPCRequest& params); extern UniValue getsparkanonymityset(const JSONRPCRequest& params); +extern UniValue getsparkanonymitysetmeta(const JSONRPCRequest& params); +extern UniValue getsparkanonymitysetsector(const JSONRPCRequest& params); extern UniValue getsparkmintmetadata(const JSONRPCRequest& params); extern UniValue getusedcoinstags(const JSONRPCRequest& params); extern UniValue getusedcoinstagstxhashes(const JSONRPCRequest& params); diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 4eebb613e4..c45d629cb6 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1330,6 +1330,85 @@ void CSparkState::GetCoinsForRecovery( } } +void CSparkState::GetAnonSetMetaData( + CChain *chain, + int maxHeight, + int coinGroupID, + uint256& blockHash_out, + std::vector& setHash_out, + int& size) { + if (coinGroups.count(coinGroupID) == 0) { + return; + } + SparkCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; + size = 0; + for (CBlockIndex *block = coinGroup.lastBlock;; block = block->pprev) { + // check coins in group coinGroupID - 1 in the case that using coins from prev group. + int id = 0; + if (CountCoinInBlock(block, coinGroupID)) { + id = coinGroupID; + } else if (CountCoinInBlock(block, coinGroupID - 1)) { + id = coinGroupID - 1; + } + if (id) { + if (size == 0) { + // latest block satisfying given conditions + // remember block hash and set hash + blockHash_out = block->GetBlockHash(); + setHash_out = GetAnonymitySetHash(block, id); + } + size += block->sparkMintedCoins[id].size(); + } + if (block == coinGroup.firstBlock) { + break ; + } + } +} + +void CSparkState::GetCoinsForRecovery( + CChain *chain, + int maxHeight, + int coinGroupID, + uint256& blockHash, + std::vector>>>& coins) { + coins.clear(); + if (coinGroups.count(coinGroupID) == 0) { + return; + } + SparkCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; + CBlockIndex *index = coinGroup.lastBlock; + // find index for block with hash of accumulatorBlockHash or set index to the coinGroup.firstBlock if not found + while (index != coinGroup.firstBlock && index->GetBlockHash() != blockHash) + index = index->pprev; + + for (CBlockIndex *block = index;; block = block->pprev) { + // ignore block heigher than max height + if (block->nHeight > maxHeight) { + continue; + } + + // check coins in group coinGroupID - 1 in the case that using coins from prev group. + int id = 0; + if (CountCoinInBlock(block, coinGroupID)) { + id = coinGroupID; + } else if (CountCoinInBlock(block, coinGroupID - 1)) { + id = coinGroupID - 1; + } + if (id) { + if (block->sparkMintedCoins.count(id) > 0) { + for (const auto &coin : block->sparkMintedCoins[id]) { + std::pair> txHashContext; + if (block->sparkTxHashContext.count(coin.S)) + txHashContext = block->sparkTxHashContext[coin.S]; + coins.push_back({coin, txHashContext}); + } + } + } + if (block == coinGroup.firstBlock) { + break ; + } + } +} std::unordered_map const & CSparkState::GetMints() const { return mintedCoins; diff --git a/src/spark/state.h b/src/spark/state.h index c8883875ef..03396a7f4b 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -213,6 +213,21 @@ class CSparkState { std::vector>>>& coins, std::vector& setHash_out); + void GetAnonSetMetaData( + CChain *chain, + int maxHeight, + int coinGroupID, + uint256& blockHash_out, + std::vector& setHash_out, + int& size); + + void GetCoinsForRecovery( + CChain *chain, + int maxHeight, + int coinGroupID, + uint256& blockHash, + std::vector>>>& coins); + std::unordered_map const & GetMints() const; std::unordered_map const & GetSpends() const; std::unordered_map const& GetSpendTxIds() const;