From fe5f6c6bed3e09050ab54b22db0d6b190a3b9b0b Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Sat, 14 Dec 2024 09:36:38 +0400 Subject: [PATCH 1/3] Implement new rpc calls to get anonymity sets --- src/rpc/misc.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++++ src/rpc/server.cpp | 2 + src/rpc/server.h | 2 + src/spark/state.cpp | 79 ++++++++++++++++++++++++ src/spark/state.h | 15 +++++ 5 files changed, 241 insertions(+) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 59705edb9f..79bcbc6334 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1307,6 +1307,147 @@ 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; + continue; + } + if (counter >= endIndex) { + break; + } + 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); + + ++counter; + } + + ret.push_back(Pair("coins", mints)); + + return ret; +} + UniValue getsparkmintmetadata(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) @@ -1921,6 +2062,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; From b50f94b2b26726e7f936ebacdcd25289df6df7a5 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 16 Dec 2024 09:02:32 +0400 Subject: [PATCH 2/3] Error case handling --- src/rpc/misc.cpp | 30 ++++++++++++++++++++---------- src/spark/state.cpp | 6 +++++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 79bcbc6334..fc1f518b06 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1371,7 +1371,7 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) "\nArguments:\n" "{\n" " \"coinGroupId\" (int)\n" - " \"latestBlock\" (string)\n" + " \"latestBlock\" (string) it should be encoded in base64 format\n" " \"startIndex\" (int)\n" " \"endIndex\" (int)\n" "}\n" @@ -1379,8 +1379,8 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) "{\n" " \"mints\" (Pair) Serialized Spark coin paired with txhash\n" "}\n" - + HelpExampleCli("getsparkanonymitysetsector", "\"1\" " "\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"" "\"0\" " "\"1000\" ") - + HelpExampleRpc("getsparkanonymitysetsector", "\"1\" " "\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"" "\"0\" " "\"1000\" ") + + HelpExampleCli("getsparkanonymitysetsector", "\"1\" " "\"Gy3sLu3zrVdJwaK6ZzM/1zdJy7hji9xT6l4FSrWgFUM=\" " "\"0\" " "\"1000\" ") + + HelpExampleRpc("getsparkanonymitysetsector", "\"1\" " "\"Gy3sLu3zrVdJwaK6ZzM/1zdJy7hji9xT6l4FSrWgFUM=\" " "\"0\" " "\"1000\" ") ); @@ -1403,16 +1403,26 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) throw std::runtime_error(std::string("Please rerun Firo with -mobile ")); } std::vector>>> coins; - uint256 blockHash = uint256S(latestBlock); + + std::string strHash = DecodeBase64(latestBlock); + std::vector vec(strHash.begin(), strHash.end()); + if (vec.size() != 32) + throw std::runtime_error(std::string("Provided blockHash data is not correct.")); + + uint256 blockHash(vec); { LOCK(cs_main); spark::CSparkState* sparkState = spark::CSparkState::GetState(); - sparkState->GetCoinsForRecovery( - &chainActive, - chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), - coinGroupId, - blockHash, - coins); + try { + sparkState->GetCoinsForRecovery( + &chainActive, + chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), + coinGroupId, + blockHash, + coins); + } catch (std::exception & e) { + throw std::runtime_error(std::string("Unable to get anonymity set by provided parameters: ") + e.what()); + } } UniValue ret(UniValue::VOBJ); diff --git a/src/spark/state.cpp b/src/spark/state.cpp index c45d629cb6..d80606fe20 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1373,7 +1373,7 @@ void CSparkState::GetCoinsForRecovery( std::vector>>>& coins) { coins.clear(); if (coinGroups.count(coinGroupID) == 0) { - return; + throw std::runtime_error(std::string("There is no anonymity set with this id: " + coinGroupID)); } SparkCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; CBlockIndex *index = coinGroup.lastBlock; @@ -1381,6 +1381,10 @@ void CSparkState::GetCoinsForRecovery( while (index != coinGroup.firstBlock && index->GetBlockHash() != blockHash) index = index->pprev; + if (index == coinGroup.firstBlock && coinGroup.firstBlock != coinGroup.lastBlock) + throw std::runtime_error(std::string("Incorrect blockHash provided: " + blockHash.GetHex())); + + for (CBlockIndex *block = index;; block = block->pprev) { // ignore block heigher than max height if (block->nHeight > maxHeight) { From 600da33369de52812c83fe574d4470ba3aaa5114 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Tue, 17 Dec 2024 18:04:34 +0400 Subject: [PATCH 3/3] Optimize getting sector --- src/rpc/misc.cpp | 13 +++---------- src/spark/state.cpp | 14 ++++++++++++-- src/spark/state.h | 2 ++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index fc1f518b06..c7c58adb2d 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1418,6 +1418,8 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) &chainActive, chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), coinGroupId, + startIndex, + endIndex, blockHash, coins); } catch (std::exception & e) { @@ -1428,15 +1430,8 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); UniValue mints(UniValue::VARR); - std::size_t counter = 0; + for (const auto& coin : coins) { - if (counter < startIndex) { - ++counter; - continue; - } - if (counter >= endIndex) { - break; - } CDataStream serializedCoin(SER_NETWORK, PROTOCOL_VERSION); serializedCoin << coin; std::vector vch(serializedCoin.begin(), serializedCoin.end()); @@ -1449,8 +1444,6 @@ UniValue getsparkanonymitysetsector(const JSONRPCRequest& request) UniValue entity(UniValue::VARR); entity.push_backV(data); mints.push_back(entity); - - ++counter; } ret.push_back(Pair("coins", mints)); diff --git a/src/spark/state.cpp b/src/spark/state.cpp index d80606fe20..dcfb63bbcc 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1369,6 +1369,8 @@ void CSparkState::GetCoinsForRecovery( CChain *chain, int maxHeight, int coinGroupID, + int startIndex, + int endIndex, uint256& blockHash, std::vector>>>& coins) { coins.clear(); @@ -1384,7 +1386,7 @@ void CSparkState::GetCoinsForRecovery( if (index == coinGroup.firstBlock && coinGroup.firstBlock != coinGroup.lastBlock) throw std::runtime_error(std::string("Incorrect blockHash provided: " + blockHash.GetHex())); - + std::size_t counter = 0; for (CBlockIndex *block = index;; block = block->pprev) { // ignore block heigher than max height if (block->nHeight > maxHeight) { @@ -1401,14 +1403,22 @@ void CSparkState::GetCoinsForRecovery( if (id) { if (block->sparkMintedCoins.count(id) > 0) { for (const auto &coin : block->sparkMintedCoins[id]) { + if (counter < startIndex) { + ++counter; + continue; + } + if (counter >= endIndex) { + break; + } std::pair> txHashContext; if (block->sparkTxHashContext.count(coin.S)) txHashContext = block->sparkTxHashContext[coin.S]; coins.push_back({coin, txHashContext}); + ++counter; } } } - if (block == coinGroup.firstBlock) { + if (block == coinGroup.firstBlock || counter >= endIndex) { break ; } } diff --git a/src/spark/state.h b/src/spark/state.h index 03396a7f4b..13f72cc451 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -225,6 +225,8 @@ class CSparkState { CChain *chain, int maxHeight, int coinGroupID, + int startIndex, + int endIndex, uint256& blockHash, std::vector>>>& coins);