mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
feat(rpc): Asset Unlock status by index (#5776)
## Issue being fixed or feature implemented Platform in the scope of credit withdrawals, need a way to get the status of an Asset Unlock by index. ## What was done? A new RPC was created `getassetunlockchainlocks` that accepts Asset Unlock indexes array as parameter and return corresponding status for each index. The possible outcomes per each index are: - `chainlocked`: If the Asset Unlock index is mined on a Chainlocked block. - `mined`: If no Chainlock information is available, and the Asset Unlock index is mined. - `mempooled`: If the Asset Unlock index is in the mempool. - `unknown`: If none of the above are valid. Note: This RPC is whitelisted for the Platform RPC user. ## How Has This Been Tested? Inserted on `feature_asset_locks.py` covering cases where Asset Unlock txs are in mempool, mined and not present. ## Breaking Changes no ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: thephez <thephez@users.noreply.github.com> Co-authored-by: Konstantin Akimov <knstqq@gmail.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: pasta <pasta@dashboost.org>
This commit is contained in:
parent
b5dc598525
commit
563cc34b4e
11
doc/release-notes-5776.md
Normal file
11
doc/release-notes-5776.md
Normal file
@ -0,0 +1,11 @@
|
||||
Added RPC
|
||||
--------
|
||||
|
||||
- `getassetunlockstatuses` RPC allows fetching of Asset Unlock txs by their withdrawal index. The RPC accepts an array of indexes and returns status for each index.
|
||||
The possible outcomes per each index are:
|
||||
- "chainlocked": If the Asset Unlock index is mined on a ChainLocked block.
|
||||
- "mined": If no ChainLock information is available, and the Asset Unlock index is mined.
|
||||
- "mempooled": If the Asset Unlock index is in the mempool.
|
||||
- "unknown": If none of the above are valid.
|
||||
|
||||
Note: This RPC is whitelisted for the Platform RPC user.
|
@ -340,6 +340,7 @@ void PrepareShutdown(NodeContext& node)
|
||||
llmq::quorumSnapshotManager.reset();
|
||||
deterministicMNManager.reset();
|
||||
creditPoolManager.reset();
|
||||
node.creditPoolManager = nullptr;
|
||||
node.mnhf_manager.reset();
|
||||
node.evodb.reset();
|
||||
}
|
||||
@ -1926,6 +1927,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
|
||||
node.mnhf_manager.reset();
|
||||
node.mnhf_manager = std::make_unique<CMNHFManager>(*node.evodb);
|
||||
|
||||
|
||||
chainman.Reset();
|
||||
chainman.InitializeChainstate(Assert(node.mempool.get()), *node.mnhf_manager, *node.evodb, llmq::chainLocksHandler, llmq::quorumInstantSendManager, llmq::quorumBlockProcessor);
|
||||
chainman.m_total_coinstip_cache = nCoinCacheUsage;
|
||||
@ -1942,7 +1944,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
|
||||
deterministicMNManager.reset();
|
||||
deterministicMNManager.reset(new CDeterministicMNManager(chainman.ActiveChainstate(), *node.connman, *node.evodb));
|
||||
creditPoolManager.reset();
|
||||
creditPoolManager.reset(new CCreditPoolManager(*node.evodb));
|
||||
creditPoolManager = std::make_unique<CCreditPoolManager>(*node.evodb);
|
||||
node.creditPoolManager = creditPoolManager.get();
|
||||
llmq::quorumSnapshotManager.reset();
|
||||
llmq::quorumSnapshotManager.reset(new llmq::CQuorumSnapshotManager(*node.evodb));
|
||||
|
||||
|
@ -60,7 +60,7 @@ struct NodeContext {
|
||||
std::function<void()> rpc_interruption_point = [] {};
|
||||
//! Dash
|
||||
std::unique_ptr<LLMQContext> llmq_ctx;
|
||||
std::unique_ptr<CCreditPoolManager> creditPoolManager;
|
||||
CCreditPoolManager* creditPoolManager;
|
||||
std::unique_ptr<CMNHFManager> mnhf_manager;
|
||||
std::unique_ptr<CJContext> cj_ctx;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <evo/creditpool.h>
|
||||
#include <index/txindex.h>
|
||||
#include <init.h>
|
||||
#include <key_io.h>
|
||||
@ -40,11 +41,13 @@
|
||||
#include <validationinterface.h>
|
||||
#include <util/irange.h>
|
||||
|
||||
#include <evo/cbtx.h>
|
||||
#include <evo/specialtx.h>
|
||||
|
||||
#include <llmq/chainlocks.h>
|
||||
#include <llmq/context.h>
|
||||
#include <llmq/instantsend.h>
|
||||
#include <llmq/utils.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <stdint.h>
|
||||
@ -336,6 +339,118 @@ static UniValue gettxchainlocks(const JSONRPCRequest& request)
|
||||
return result_arr;
|
||||
}
|
||||
|
||||
static void getassetunlockstatuses_help(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{
|
||||
"getassetunlockstatuses",
|
||||
"\nReturns the status of given Asset Unlock indexes.\n",
|
||||
{
|
||||
{"indexes", RPCArg::Type::ARR, RPCArg::Optional::NO, "The Asset Unlock indexes (no more than 100)",
|
||||
{
|
||||
{"index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "An Asset Unlock index"},
|
||||
},
|
||||
},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::NUM, "index", "The Asset Unlock index"},
|
||||
{RPCResult::Type::STR, "status", "Status of the Asset Unlock index: {chainlocked|mined|mempooled|unknown}"},
|
||||
}},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
HelpExampleCli("getassetunlockstatuses", "'[\"myindex\",...]'")
|
||||
+ HelpExampleRpc("getassetunlockstatuses", "[\"myindex\",...]")
|
||||
},
|
||||
}.Check(request);
|
||||
}
|
||||
|
||||
static UniValue getassetunlockstatuses(const JSONRPCRequest& request)
|
||||
{
|
||||
getassetunlockstatuses_help(request);
|
||||
|
||||
const NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||
const CTxMemPool& mempool = EnsureMemPool(node);
|
||||
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
|
||||
const ChainstateManager& chainman = EnsureChainman(node);
|
||||
|
||||
UniValue result_arr(UniValue::VARR);
|
||||
const UniValue str_indexes = request.params[0].get_array();
|
||||
if (str_indexes.size() > 100) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 indexes only");
|
||||
}
|
||||
|
||||
if (g_txindex) {
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
}
|
||||
|
||||
const CBlockIndex* pTipBlockIndex{WITH_LOCK(cs_main, return chainman.ActiveChain().Tip())};
|
||||
|
||||
if (!pTipBlockIndex) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain");
|
||||
}
|
||||
|
||||
const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* {
|
||||
if (llmq_ctx.clhandler->GetBestChainLock().IsNull()) {
|
||||
// If no CL info is available, try to use CbTx CL information
|
||||
if (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) {
|
||||
return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
// We need in 2 credit pools: at tip of chain and on best CL to know if tx is mined or chainlocked
|
||||
// Sometimes that's two different blocks, sometimes not and we need to initialize 2nd creditPoolManager
|
||||
std::optional<CCreditPool> poolCL = pBlockIndexBestCL ?
|
||||
std::make_optional(node.creditPoolManager->GetCreditPool(pBlockIndexBestCL, Params().GetConsensus())) :
|
||||
std::nullopt;
|
||||
auto poolOnTip = [&]() -> std::optional<CCreditPool> {
|
||||
if (pTipBlockIndex != pBlockIndexBestCL) {
|
||||
return std::make_optional(node.creditPoolManager->GetCreditPool(pTipBlockIndex, Params().GetConsensus()));
|
||||
}
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
for (const auto i : irange::range(str_indexes.size())) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
uint64_t index{};
|
||||
if (!ParseUInt64(str_indexes[i].get_str(), &index)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid index");
|
||||
}
|
||||
obj.pushKV("index", index);
|
||||
auto status_to_push = [&]() -> std::string {
|
||||
if (poolCL.has_value() && poolCL->indexes.Contains(index)) {
|
||||
return "chainlocked";
|
||||
}
|
||||
if (poolOnTip.has_value() && poolOnTip->indexes.Contains(index)) {
|
||||
return "mined";
|
||||
}
|
||||
bool is_mempooled = [&]() {
|
||||
LOCK(mempool.cs);
|
||||
return std::any_of(mempool.mapTx.begin(), mempool.mapTx.end(), [index](const CTxMemPoolEntry &e) {
|
||||
if (e.GetTx().nType == CAssetUnlockPayload::SPECIALTX_TYPE) {
|
||||
if (CAssetUnlockPayload assetUnlockTx; GetTxPayload(e.GetTx(), assetUnlockTx)) {
|
||||
return index == assetUnlockTx.getIndex();
|
||||
} else {
|
||||
throw JSONRPCError(RPC_TRANSACTION_ERROR, "bad-assetunlocktx-payload");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}();
|
||||
return is_mempooled ? "mempooled" : "unknown";
|
||||
};
|
||||
obj.pushKV("status", status_to_push());
|
||||
result_arr.push_back(obj);
|
||||
}
|
||||
|
||||
return result_arr;
|
||||
}
|
||||
|
||||
static UniValue gettxoutproof(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"gettxoutproof",
|
||||
@ -1757,6 +1872,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t)
|
||||
static const CRPCCommand commands[] =
|
||||
{ // category name actor (function) argNames
|
||||
// --------------------- ------------------------ ----------------------- ----------
|
||||
{ "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes"} },
|
||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
|
||||
{ "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} },
|
||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} },
|
||||
|
@ -141,6 +141,7 @@ std::string CRPCTable::help(const std::string& strCommand, const std::string& st
|
||||
void CRPCTable::InitPlatformRestrictions()
|
||||
{
|
||||
mapPlatformRestrictions = {
|
||||
{"getassetunlockstatuses", {}},
|
||||
{"getbestblockhash", {}},
|
||||
{"getblockhash", {}},
|
||||
{"getblockcount", {}},
|
||||
|
@ -169,6 +169,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
|
||||
connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
|
||||
llmq::quorumSnapshotManager.reset(new llmq::CQuorumSnapshotManager(*m_node.evodb));
|
||||
creditPoolManager = std::make_unique<CCreditPoolManager>(*m_node.evodb);
|
||||
m_node.creditPoolManager = creditPoolManager.get();
|
||||
static bool noui_connected = false;
|
||||
if (!noui_connected) {
|
||||
noui_connect();
|
||||
@ -182,6 +183,7 @@ BasicTestingSetup::~BasicTestingSetup()
|
||||
connman.reset();
|
||||
llmq::quorumSnapshotManager.reset();
|
||||
creditPoolManager.reset();
|
||||
m_node.creditPoolManager = nullptr;
|
||||
m_node.mnhf_manager.reset();
|
||||
m_node.evodb.reset();
|
||||
|
||||
@ -215,7 +217,9 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
|
||||
::mmetaman = std::make_unique<CMasternodeMetaMan>(/* load_cache */ false);
|
||||
::netfulfilledman = std::make_unique<CNetFulfilledRequestManager>(/* load_cache */ false);
|
||||
|
||||
m_node.creditPoolManager = std::make_unique<CCreditPoolManager>(*m_node.evodb);
|
||||
creditPoolManager = std::make_unique<CCreditPoolManager>(*m_node.evodb);
|
||||
m_node.creditPoolManager = creditPoolManager.get();
|
||||
|
||||
|
||||
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
|
||||
constexpr int script_check_threads = 2;
|
||||
@ -226,7 +230,8 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
|
||||
ChainTestingSetup::~ChainTestingSetup()
|
||||
{
|
||||
m_node.scheduler->stop();
|
||||
m_node.creditPoolManager.reset();
|
||||
creditPoolManager.reset();
|
||||
m_node.creditPoolManager = nullptr;
|
||||
StopScriptCheckWorkerThreads();
|
||||
GetMainSignals().FlushBackgroundCallbacks();
|
||||
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
||||
|
@ -330,6 +330,10 @@ class AssetLocksTest(DashTestFramework):
|
||||
|
||||
txid = self.send_tx(asset_unlock_tx)
|
||||
assert "assetUnlockTx" in node.getrawtransaction(txid, 1)
|
||||
|
||||
indexes_statuses = self.nodes[0].getassetunlockstatuses(["101", "102", "300"])
|
||||
assert_equal([{'index': 101, 'status': 'mempooled'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses)
|
||||
|
||||
self.mempool_size += 1
|
||||
self.check_mempool_size()
|
||||
self.validate_credit_pool_balance(locked_1)
|
||||
@ -502,6 +506,9 @@ class AssetLocksTest(DashTestFramework):
|
||||
node.generate(1)
|
||||
self.sync_all()
|
||||
|
||||
indexes_statuses = self.nodes[0].getassetunlockstatuses(["101", "102", "103"])
|
||||
assert_equal([{'index': 101, 'status': 'mined'}, {'index': 102, 'status': 'mined'}, {'index': 103, 'status': 'unknown'}], indexes_statuses)
|
||||
|
||||
self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...")
|
||||
self.slowly_generate_batch(60)
|
||||
self.log.info("Checking that credit pool is not changed...")
|
||||
|
@ -56,7 +56,8 @@ class HTTPBasicsTest(BitcoinTestFramework):
|
||||
assert_equal(resp.status, expexted_status)
|
||||
conn.close()
|
||||
|
||||
whitelisted = ["getbestblockhash",
|
||||
whitelisted = ["getassetunlockstatuses",
|
||||
"getbestblockhash",
|
||||
"getblockhash",
|
||||
"getblockcount",
|
||||
"getbestchainlock",
|
||||
|
Loading…
Reference in New Issue
Block a user