mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
feat(rpc): added optional block height in getassetunlockstatuses
(#5849)
## Issue being fixed or feature implemented RPC `getassetunlockstatuses` is now accepting an extra optional parameter `height`. When a valid `height` is passed, then the RPC returns the status of AssetUnlock indexes up to this specific block. (Requested by Platform team) ## What was done? Note that in order to avoid cases that can lead to deterministic result, when `height` is passed, then the only `chainlocked` and `unknown` outcomes are possible. ## How Has This Been Tested? `feature_asset_locks.py` was updated. ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] 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: Konstantin Akimov <knstqq@gmail.com> Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
2238e03bae
commit
82310b0984
@ -1,11 +1,14 @@
|
|||||||
Added RPC
|
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.
|
- `getassetunlockstatuses` RPC allows fetching of Asset Unlock txs by their withdrawal index.
|
||||||
|
The RPC accepts an array of indexes and an optional block height.
|
||||||
The possible outcomes per each index are:
|
The possible outcomes per each index are:
|
||||||
- "chainlocked": If the Asset Unlock index is mined on a ChainLocked block.
|
- `chainlocked`: If the Asset Unlock index is mined on a ChainLocked block or up to the given block height.
|
||||||
- "mined": If no ChainLock information is available, and the Asset Unlock index is mined.
|
- `mined`: If no ChainLock information is available for the mined Asset Unlock index.
|
||||||
- "mempooled": If the Asset Unlock index is in the mempool.
|
- `mempooled`: If the Asset Unlock index is in the mempool.
|
||||||
- "unknown": If none of the above are valid.
|
- `unknown`: If none of the above are valid.
|
||||||
|
|
||||||
|
Note: If a specific block height is passed on request, then only `chainlocked` and `unknown` outcomes are possible.
|
||||||
|
|
||||||
Note: This RPC is whitelisted for the Platform RPC user.
|
Note: This RPC is whitelisted for the Platform RPC user.
|
||||||
|
@ -58,6 +58,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "listreceivedbylabel", 1, "addlocked" },
|
{ "listreceivedbylabel", 1, "addlocked" },
|
||||||
{ "listreceivedbylabel", 2, "include_empty" },
|
{ "listreceivedbylabel", 2, "include_empty" },
|
||||||
{ "listreceivedbylabel", 3, "include_watchonly" },
|
{ "listreceivedbylabel", 3, "include_watchonly" },
|
||||||
|
{ "getassetunlockstatuses", 0, "indexes" },
|
||||||
|
{ "getassetunlockstatuses", 1, "height" },
|
||||||
{ "getbalance", 1, "minconf" },
|
{ "getbalance", 1, "minconf" },
|
||||||
{ "getbalance", 2, "addlocked" },
|
{ "getbalance", 2, "addlocked" },
|
||||||
{ "getbalance", 3, "include_watchonly" },
|
{ "getbalance", 3, "include_watchonly" },
|
||||||
|
@ -438,13 +438,14 @@ static void getassetunlockstatuses_help(const JSONRPCRequest& request)
|
|||||||
{
|
{
|
||||||
RPCHelpMan{
|
RPCHelpMan{
|
||||||
"getassetunlockstatuses",
|
"getassetunlockstatuses",
|
||||||
"\nReturns the status of given Asset Unlock indexes.\n",
|
"\nReturns the status of given Asset Unlock indexes at the tip of the chain or at a specific block height if specified.\n",
|
||||||
{
|
{
|
||||||
{"indexes", RPCArg::Type::ARR, RPCArg::Optional::NO, "The Asset Unlock indexes (no more than 100)",
|
{"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"},
|
{"index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "An Asset Unlock index"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The maximum block height to check"},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids",
|
RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids",
|
||||||
@ -488,27 +489,43 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request)
|
|||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<CCreditPool> poolCL{std::nullopt};
|
||||||
|
std::optional<CCreditPool> poolOnTip{std::nullopt};
|
||||||
|
std::optional<int> nSpecificCoreHeight{std::nullopt};
|
||||||
|
|
||||||
|
if (!request.params[1].isNull()) {
|
||||||
|
nSpecificCoreHeight = request.params[1].get_int();
|
||||||
|
if (nSpecificCoreHeight.value() < 0 || nSpecificCoreHeight.value() > chainman.ActiveChain().Height()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
||||||
|
}
|
||||||
|
poolCL = std::make_optional(node.creditPoolManager->GetCreditPool(chainman.ActiveChain()[nSpecificCoreHeight.value()], Params().GetConsensus()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* {
|
const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* {
|
||||||
if (llmq_ctx.clhandler->GetBestChainLock().IsNull()) {
|
if (!llmq_ctx.clhandler->GetBestChainLock().IsNull()) {
|
||||||
|
return pTipBlockIndex->GetAncestor(llmq_ctx.clhandler->GetBestChainLock().getHeight());
|
||||||
|
}
|
||||||
// If no CL info is available, try to use CbTx CL information
|
// If no CL info is available, try to use CbTx CL information
|
||||||
if (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) {
|
if (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) {
|
||||||
return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1);
|
return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1);
|
||||||
}
|
}
|
||||||
}
|
// no CL info, no CbTx CL
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// We need in 2 credit pools: at tip of chain and on best CL to know if tx is mined or chainlocked
|
// 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
|
// Sometimes that's two different blocks, sometimes not and we need to initialize 2nd creditPoolManager
|
||||||
std::optional<CCreditPool> poolCL = pBlockIndexBestCL ?
|
poolCL = pBlockIndexBestCL ?
|
||||||
std::make_optional(node.creditPoolManager->GetCreditPool(pBlockIndexBestCL, Params().GetConsensus())) :
|
std::make_optional(node.creditPoolManager->GetCreditPool(pBlockIndexBestCL, Params().GetConsensus())) :
|
||||||
std::nullopt;
|
std::nullopt;
|
||||||
auto poolOnTip = [&]() -> std::optional<CCreditPool> {
|
|
||||||
|
poolOnTip = [&]() -> std::optional<CCreditPool> {
|
||||||
if (pTipBlockIndex != pBlockIndexBestCL) {
|
if (pTipBlockIndex != pBlockIndexBestCL) {
|
||||||
return std::make_optional(node.creditPoolManager->GetCreditPool(pTipBlockIndex, Params().GetConsensus()));
|
return std::make_optional(node.creditPoolManager->GetCreditPool(pTipBlockIndex, Params().GetConsensus()));
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}();
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto i : irange::range(str_indexes.size())) {
|
for (const auto i : irange::range(str_indexes.size())) {
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
@ -537,7 +554,7 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request)
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}();
|
}();
|
||||||
return is_mempooled ? "mempooled" : "unknown";
|
return is_mempooled && !nSpecificCoreHeight.has_value() ? "mempooled" : "unknown";
|
||||||
};
|
};
|
||||||
obj.pushKV("status", status_to_push());
|
obj.pushKV("status", status_to_push());
|
||||||
result_arr.push_back(obj);
|
result_arr.push_back(obj);
|
||||||
@ -1975,7 +1992,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t)
|
|||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
// --------------------- ------------------------ ----------------------- ----------
|
// --------------------- ------------------------ ----------------------- ----------
|
||||||
{ "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes"} },
|
{ "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes","height"} },
|
||||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
|
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
|
||||||
{ "rawtransactions", "getrawtransactionmulti", &getrawtransactionmulti, {"txid_map","verbose"} },
|
{ "rawtransactions", "getrawtransactionmulti", &getrawtransactionmulti, {"txid_map","verbose"} },
|
||||||
{ "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} },
|
{ "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} },
|
||||||
|
@ -349,8 +349,12 @@ class AssetLocksTest(DashTestFramework):
|
|||||||
txid = self.send_tx(asset_unlock_tx)
|
txid = self.send_tx(asset_unlock_tx)
|
||||||
assert "assetUnlockTx" in node.getrawtransaction(txid, 1)
|
assert "assetUnlockTx" in node.getrawtransaction(txid, 1)
|
||||||
|
|
||||||
indexes_statuses = self.nodes[0].getassetunlockstatuses(["101", "102", "300"])
|
tip = self.nodes[0].getblockcount()
|
||||||
assert_equal([{'index': 101, 'status': 'mempooled'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses)
|
indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"])
|
||||||
|
assert_equal([{'index': 101, 'status': 'mempooled'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_no_height)
|
||||||
|
indexes_statuses_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"], tip)
|
||||||
|
assert_equal([{'index': 101, 'status': 'unknown'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_height)
|
||||||
|
|
||||||
|
|
||||||
self.mempool_size += 1
|
self.mempool_size += 1
|
||||||
self.check_mempool_size()
|
self.check_mempool_size()
|
||||||
@ -524,8 +528,12 @@ class AssetLocksTest(DashTestFramework):
|
|||||||
node.generate(1)
|
node.generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
indexes_statuses = self.nodes[0].getassetunlockstatuses(["101", "102", "103"])
|
tip = self.nodes[0].getblockcount()
|
||||||
assert_equal([{'index': 101, 'status': 'mined'}, {'index': 102, 'status': 'mined'}, {'index': 103, 'status': 'unknown'}], indexes_statuses)
|
indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "103"])
|
||||||
|
assert_equal([{'index': 101, 'status': 'mined'}, {'index': 102, 'status': 'mined'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_no_height)
|
||||||
|
indexes_statuses_height = self.nodes[0].getassetunlockstatuses(["101", "102", "103"], tip)
|
||||||
|
assert_equal([{'index': 101, 'status': 'chainlocked'}, {'index': 102, 'status': 'chainlocked'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_height)
|
||||||
|
|
||||||
|
|
||||||
self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...")
|
self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...")
|
||||||
self.slowly_generate_batch(60)
|
self.slowly_generate_batch(60)
|
||||||
|
Loading…
Reference in New Issue
Block a user