From c597fb4d81e93f471f774a289632cacb7300f464 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sun, 28 Jan 2024 20:10:15 +0200 Subject: [PATCH 1/9] fix for chainlocked case --- src/rpc/rawtransaction.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index be9301ee82810..3fc7df57a52b7 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -495,6 +495,9 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1); } } + else { + return pTipBlockIndex->GetAncestor(llmq_ctx.clhandler->GetBestChainLock().getHeight()); + } return nullptr; }(); From 2c150c6bfb515d120a9b87e4794794fcfd9fecb6 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 29 Jan 2024 11:50:07 +0200 Subject: [PATCH 2/9] suggestions --- src/rpc/rawtransaction.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 3fc7df57a52b7..9c21cbd1ee224 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -489,15 +489,14 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) } 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); - } - } - else { + 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 (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) { + return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1); + } + // no CL info, no CbTx CL return nullptr; }(); From f5175bf99e948e60750ae5d1f7da6d11a854caf8 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 29 Jan 2024 15:47:01 +0200 Subject: [PATCH 3/9] added specific block height request --- doc/release-notes-5776.md | 13 +++++--- src/rpc/client.cpp | 2 ++ src/rpc/rawtransaction.cpp | 62 ++++++++++++++++++++++++-------------- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/doc/release-notes-5776.md b/doc/release-notes-5776.md index ac1e102fa1666..19985f44e2c84 100644 --- a/doc/release-notes-5776.md +++ b/doc/release-notes-5776.md @@ -1,11 +1,14 @@ 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 optionally a block height. 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. +- `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. +- `mempooled`: If the Asset Unlock index is in the mempool. +- `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. diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index e9050e41ca9f3..52eb7bb4d06a8 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -58,6 +58,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listreceivedbylabel", 1, "addlocked" }, { "listreceivedbylabel", 2, "include_empty" }, { "listreceivedbylabel", 3, "include_watchonly" }, + { "getassetunlockstatuses", 0, "indexes" }, + { "getassetunlockstatuses", 1, "height" }, { "getbalance", 1, "minconf" }, { "getbalance", 2, "addlocked" }, { "getbalance", 3, "include_watchonly" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 9c21cbd1ee224..4e8153a74541c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -445,6 +445,7 @@ static void getassetunlockstatuses_help(const JSONRPCRequest& request) {"index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "An Asset Unlock index"}, }, }, + {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The height index"}, }, RPCResult{ RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids", @@ -488,29 +489,44 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain"); } - const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* { - 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 (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) { - return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1); - } - // no CL info, no CbTx CL - 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 poolCL = pBlockIndexBestCL ? - std::make_optional(node.creditPoolManager->GetCreditPool(pBlockIndexBestCL, Params().GetConsensus())) : - std::nullopt; - auto poolOnTip = [&]() -> std::optional { - if (pTipBlockIndex != pBlockIndexBestCL) { - return std::make_optional(node.creditPoolManager->GetCreditPool(pTipBlockIndex, Params().GetConsensus())); + std::optional poolCL = std::nullopt; + std::optional poolOnTip = std::nullopt; + bool fSpecificCoreHeight = false; + + if (!request.params[1].isNull()) { + int nHeight = request.params[1].get_int(); + if (nHeight < 0 || nHeight > chainman.ActiveChain().Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } - return std::nullopt; - }(); + fSpecificCoreHeight = true; + poolCL = std::make_optional(node.creditPoolManager->GetCreditPool(chainman.ActiveChain()[nHeight], Params().GetConsensus())); + } + else { + const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* { + 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 (const auto cbtx_best_cl = GetNonNullCoinbaseChainlock(pTipBlockIndex)) { + return pTipBlockIndex->GetAncestor(pTipBlockIndex->nHeight - cbtx_best_cl->second - 1); + } + // no CL info, no CbTx CL + 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 + poolCL = pBlockIndexBestCL ? + std::make_optional(node.creditPoolManager->GetCreditPool(pBlockIndexBestCL, Params().GetConsensus())) : + std::nullopt; + + poolOnTip = [&]() -> std::optional { + 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); @@ -539,7 +555,7 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) return false; }); }(); - return is_mempooled ? "mempooled" : "unknown"; + return is_mempooled && !fSpecificCoreHeight ? "mempooled" : "unknown"; }; obj.pushKV("status", status_to_push()); result_arr.push_back(obj); From 9271345266d7e604407383905532c423cfad7e08 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 29 Jan 2024 16:31:17 +0200 Subject: [PATCH 4/9] adjusted feature_asset_locks.py --- test/functional/feature_asset_locks.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/functional/feature_asset_locks.py b/test/functional/feature_asset_locks.py index acaa158cddf8f..71d87233676cd 100755 --- a/test/functional/feature_asset_locks.py +++ b/test/functional/feature_asset_locks.py @@ -349,8 +349,12 @@ def run_test(self): 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) + tip = self.nodes[0].getblockcount() + 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.check_mempool_size() @@ -524,8 +528,12 @@ def run_test(self): 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) + tip = self.nodes[0].getblockcount() + 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.slowly_generate_batch(60) From d7408624f041766fddd181182dd57e0c531e610f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 29 Jan 2024 21:51:47 +0700 Subject: [PATCH 5/9] bump guix From 7bfa0d7e3e46b90c9f52986abdcfaaae3f672411 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 29 Jan 2024 18:30:50 +0200 Subject: [PATCH 6/9] Better doc --- src/rpc/rawtransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4e8153a74541c..7b49ef9ad8a04 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -438,7 +438,7 @@ static void getassetunlockstatuses_help(const JSONRPCRequest& request) { RPCHelpMan{ "getassetunlockstatuses", - "\nReturns the status of given Asset Unlock indexes.\n", + "\nReturns the status of given Asset Unlock indexes. Block height can be also specified\n", { {"indexes", RPCArg::Type::ARR, RPCArg::Optional::NO, "The Asset Unlock indexes (no more than 100)", { From 01a6fcf7d1b43f1f74cfd92bbc291a10599d75ee Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 29 Jan 2024 21:58:07 +0200 Subject: [PATCH 7/9] suggestions --- doc/release-notes-5776.md | 6 +++--- src/rpc/rawtransaction.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/release-notes-5776.md b/doc/release-notes-5776.md index 19985f44e2c84..e7885e3dce18c 100644 --- a/doc/release-notes-5776.md +++ b/doc/release-notes-5776.md @@ -1,11 +1,11 @@ Added RPC -------- -- `getassetunlockstatuses` RPC allows fetching of Asset Unlock txs by their withdrawal index. -The RPC accepts an array of indexes and optionally a block height. +- `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: - `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. - `unknown`: If none of the above are valid. diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7b49ef9ad8a04..1188c1c2e298b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -445,7 +445,7 @@ static void getassetunlockstatuses_help(const JSONRPCRequest& request) {"index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "An Asset Unlock index"}, }, }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The height index"}, + {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The maximum block height to check"}, }, RPCResult{ RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids", @@ -1993,7 +1993,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t) static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes"} }, + { "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes","height"} }, { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, { "rawtransactions", "getrawtransactionmulti", &getrawtransactionmulti, {"txid_map","verbose"} }, { "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} }, From 4c222b263be49cfeb047be9ad0078457d9731ae7 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 30 Jan 2024 20:50:12 +0200 Subject: [PATCH 8/9] Update src/rpc/rawtransaction.cpp Co-authored-by: UdjinM6 --- src/rpc/rawtransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 1188c1c2e298b..2501a76014af8 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -438,7 +438,7 @@ static void getassetunlockstatuses_help(const JSONRPCRequest& request) { RPCHelpMan{ "getassetunlockstatuses", - "\nReturns the status of given Asset Unlock indexes. Block height can be also specified\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)", { From 0d391ebac267e6349b0c1725d8afae16b4653b5d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 1 Feb 2024 00:48:13 +0200 Subject: [PATCH 9/9] use optional specificCoreHeight instead of bool + int --- src/rpc/rawtransaction.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 2501a76014af8..9f4779186e785 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -489,17 +489,16 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) throw JSONRPCError(RPC_INTERNAL_ERROR, "No blocks in chain"); } - std::optional poolCL = std::nullopt; - std::optional poolOnTip = std::nullopt; - bool fSpecificCoreHeight = false; + std::optional poolCL{std::nullopt}; + std::optional poolOnTip{std::nullopt}; + std::optional nSpecificCoreHeight{std::nullopt}; if (!request.params[1].isNull()) { - int nHeight = request.params[1].get_int(); - if (nHeight < 0 || nHeight > chainman.ActiveChain().Height()) { + 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"); } - fSpecificCoreHeight = true; - poolCL = std::make_optional(node.creditPoolManager->GetCreditPool(chainman.ActiveChain()[nHeight], Params().GetConsensus())); + poolCL = std::make_optional(node.creditPoolManager->GetCreditPool(chainman.ActiveChain()[nSpecificCoreHeight.value()], Params().GetConsensus())); } else { const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* { @@ -555,7 +554,7 @@ static UniValue getassetunlockstatuses(const JSONRPCRequest& request) return false; }); }(); - return is_mempooled && !fSpecificCoreHeight ? "mempooled" : "unknown"; + return is_mempooled && !nSpecificCoreHeight.has_value() ? "mempooled" : "unknown"; }; obj.pushKV("status", status_to_push()); result_arr.push_back(obj);