From 5f6cf645e99e33f5423d6e3e1f195c3346ee60b5 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 17 Sep 2023 17:16:59 +0700 Subject: [PATCH 01/14] wip: draft 1 --- src/rpc/client.cpp | 1 + src/rpc/rawtransaction.cpp | 95 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index cc25bff62121..35191cae1701 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -107,6 +107,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbose" }, + { "getrawtransactions", 0, "txids" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 3d99f2aae27a..4455ce855f8e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -260,6 +260,100 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); return result; } +static UniValue getrawtransactions(const JSONRPCRequest& request) +{ + RPCHelpMan{ + "getrawtransactions", + "\nReturn the raw transactions data.\n" + "\nHint: Use gettransaction for wallet transactions.\n", + { + {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction id", + { + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"}, + }, + }, + }, + RPCResult{ + RPCResult::Type::STR, "data", "." + }, + RPCExamples{ + HelpExampleCli("getrawtransactions", "\"mytxids\"") + }, + }.Check(request); + + { + std::set setTxids; + uint256 oneTxid; + LogPrintf("in-1\n"); + UniValue txids = request.params[0].get_array(); + LogPrintf("in-2\n"); + for (unsigned int idx = 0; idx < txids.size(); idx++) { + LogPrintf("ids: %d\n", idx); + const UniValue& txid = txids[idx]; + uint256 hash(ParseHashV(txid, "txid")); + if (setTxids.count(hash)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str()); + } + setTxids.insert(hash); + oneTxid = hash; + LogPrintf("hash: %s\n", hash.ToString()); + } + } + + const NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + + bool in_active_chain = true; + CBlockIndex* blockindex = nullptr; + + bool f_txindex_ready = false; + if (g_txindex && !blockindex) { + f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain(); + } + + LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CTxMemPool& mempool = EnsureMemPool(node); + + UniValue result_arr(UniValue::VARR); + UniValue txids = request.params[0].get_array(); + for (unsigned int idx = 0; idx < txids.size(); ++idx) { + try { + const UniValue& txid = txids[idx]; + uint256 hash(ParseHashV(txid, "txid")); + if (hash == Params().GenesisBlock().hashMerkleRoot) { + // Special exception for the genesis block coinbase transaction + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved"); + } + + uint256 hash_block; + const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block); + if (!tx) { + std::string errmsg; + if (blockindex) { + if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); + } + errmsg = "No such transaction found in the provided block"; + } else if (!g_txindex) { + errmsg = "No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries"; + } else if (!f_txindex_ready) { + errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed"; + } else { + errmsg = "No such mempool or blockchain transaction"; + } + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); + } + + UniValue result(UniValue::VOBJ); + if (blockindex) result.pushKV("in_active_chain", in_active_chain); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); + result_arr.push_back(result); + } catch (const UniValue& error) { + result_arr.push_back(error); + } + } + return result_arr; +} static UniValue gettxoutproof(const JSONRPCRequest& request) { @@ -1683,6 +1777,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, + { "rawtransactions", "getrawtransactions", &getrawtransactions, {"txids"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, From 5cf3dc00a711431ed15dc17e07cbbceca50a3c56 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 17 Sep 2023 17:21:02 +0700 Subject: [PATCH 02/14] cleanup --- src/rpc/rawtransaction.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4455ce855f8e..7e20f576391c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -274,32 +274,13 @@ static UniValue getrawtransactions(const JSONRPCRequest& request) }, }, RPCResult{ - RPCResult::Type::STR, "data", "." + RPCResult::Type::STR, "Array of objects from rpc result getrawtransaction with verbose on", "." }, RPCExamples{ HelpExampleCli("getrawtransactions", "\"mytxids\"") }, }.Check(request); - { - std::set setTxids; - uint256 oneTxid; - LogPrintf("in-1\n"); - UniValue txids = request.params[0].get_array(); - LogPrintf("in-2\n"); - for (unsigned int idx = 0; idx < txids.size(); idx++) { - LogPrintf("ids: %d\n", idx); - const UniValue& txid = txids[idx]; - uint256 hash(ParseHashV(txid, "txid")); - if (setTxids.count(hash)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str()); - } - setTxids.insert(hash); - oneTxid = hash; - LogPrintf("hash: %s\n", hash.ToString()); - } - } - const NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); From d70235c0bf8d2983d4df902211af1c0855138d7b Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 17 Sep 2023 18:19:36 +0700 Subject: [PATCH 03/14] rename rpc to gettransactionsarelocked; clean up unused code and data. seems works --- src/rpc/client.cpp | 2 +- src/rpc/rawtransaction.cpp | 101 ++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 35191cae1701..16fa38738b1a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -107,7 +107,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbose" }, - { "getrawtransactions", 0, "txids" }, + { "gettransactionsarelocked", 0, "txids" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7e20f576391c..03363dcf936e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -50,6 +50,34 @@ #include +static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) +{ + bool chainLock = false; + if (!hashBlock.IsNull()) { + LOCK(cs_main); + + entry.pushKV("blockhash", hashBlock.GetHex()); + CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); + if (pindex) { + if (active_chainstate.m_chain.Contains(pindex)) { + entry.pushKV("height", pindex->nHeight); + entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); + entry.pushKV("time", pindex->GetBlockTime()); + entry.pushKV("blocktime", pindex->GetBlockTime()); + chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + } else { + entry.pushKV("height", -1); + entry.pushKV("confirmations", 0); + } + } + } + + bool fLocked = isman.IsLocked(tx.GetHash()); + entry.pushKV("instantlock", fLocked || chainLock); + entry.pushKV("instantlock_internal", fLocked); + entry.pushKV("chainlock", chainLock); +} + void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. @@ -80,31 +108,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo } TxToUniv(tx, uint256(), entry, true, &txSpentInfo); - - bool chainLock = false; - if (!hashBlock.IsNull()) { - LOCK(cs_main); - - entry.pushKV("blockhash", hashBlock.GetHex()); - CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); - if (pindex) { - if (active_chainstate.m_chain.Contains(pindex)) { - entry.pushKV("height", pindex->nHeight); - entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); - entry.pushKV("time", pindex->GetBlockTime()); - entry.pushKV("blocktime", pindex->GetBlockTime()); - chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); - } else { - entry.pushKV("height", -1); - entry.pushKV("confirmations", 0); - } - } - } - - bool fLocked = isman.IsLocked(txid); - entry.pushKV("instantlock", fLocked || chainLock); - entry.pushKV("instantlock_internal", fLocked); - entry.pushKV("chainlock", chainLock); + TxLockStatusToUniv(tx, hashBlock, active_chainstate, clhandler, isman, entry); } static UniValue getrawtransaction(const JSONRPCRequest& request) @@ -260,31 +264,40 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); return result; } -static UniValue getrawtransactions(const JSONRPCRequest& request) +static UniValue gettransactionsarelocked(const JSONRPCRequest& request) { RPCHelpMan{ - "getrawtransactions", - "\nReturn the raw transactions data.\n" - "\nHint: Use gettransaction for wallet transactions.\n", + "gettransactionsarelocked", + "\nReturn the raw transactions data.\n" + "\nHint: Use gettransaction for wallet transactions.\n", + { + {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction id", { - {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction id", - { - {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"}, - }, - }, - }, - RPCResult{ - RPCResult::Type::STR, "Array of objects from rpc result getrawtransaction with verbose on", "." - }, - RPCExamples{ - HelpExampleCli("getrawtransactions", "\"mytxids\"") + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"}, }, + }, + }, + RPCResult{ + RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash"}, + {RPCResult::Type::BOOL, "instantlock", "Current transaction lock state"}, + {RPCResult::Type::BOOL, "chainlock", "he state of the corresponding block chainlock"}, + {RPCResult::Type::NUM, "height", "The block height"}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("gettransactionsarelocked", "\"mytxids\"") + + HelpExampleCli("gettransactionsarelocked", "\"[tx1, tx2, ...]\"") + }, }.Check(request); const NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); - bool in_active_chain = true; CBlockIndex* blockindex = nullptr; bool f_txindex_ready = false; @@ -293,7 +306,6 @@ static UniValue getrawtransactions(const JSONRPCRequest& request) } LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CTxMemPool& mempool = EnsureMemPool(node); UniValue result_arr(UniValue::VARR); UniValue txids = request.params[0].get_array(); @@ -326,8 +338,7 @@ static UniValue getrawtransactions(const JSONRPCRequest& request) } UniValue result(UniValue::VOBJ); - if (blockindex) result.pushKV("in_active_chain", in_active_chain); - TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); + TxLockStatusToUniv(*tx, hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); result_arr.push_back(result); } catch (const UniValue& error) { result_arr.push_back(error); @@ -1758,7 +1769,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "getrawtransactions", &getrawtransactions, {"txids"} }, + { "rawtransactions", "gettransactionsarelocked", &gettransactionsarelocked, {"txids"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, From b6001622b8d087b3be81a0e900c03893e82984a4 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 17 Sep 2023 18:28:18 +0700 Subject: [PATCH 04/14] add limiter: no more than 100 txes in one request --- src/rpc/rawtransaction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 03363dcf936e..a179103bbb11 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -271,7 +271,7 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) "\nReturn the raw transactions data.\n" "\nHint: Use gettransaction for wallet transactions.\n", { - {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction id", + {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction ids (no more than 100)", { {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"}, }, @@ -309,7 +309,7 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) UniValue result_arr(UniValue::VARR); UniValue txids = request.params[0].get_array(); - for (unsigned int idx = 0; idx < txids.size(); ++idx) { + for (size_t idx = 0; idx < std::min(100, txids.size()); ++idx) { try { const UniValue& txid = txids[idx]; uint256 hash(ParseHashV(txid, "txid")); From 2d89c9ee829b5602561965adc517053f64daa378 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 17 Sep 2023 22:43:16 +0700 Subject: [PATCH 05/14] removed some extra fields that are not needed --- src/rpc/rawtransaction.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a179103bbb11..0c4cc4c00693 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -50,7 +50,7 @@ #include -static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) +static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry, bool detailed = true) { bool chainLock = false; if (!hashBlock.IsNull()) { @@ -61,20 +61,24 @@ static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, if (pindex) { if (active_chainstate.m_chain.Contains(pindex)) { entry.pushKV("height", pindex->nHeight); - entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); - entry.pushKV("time", pindex->GetBlockTime()); - entry.pushKV("blocktime", pindex->GetBlockTime()); + if (detailed) { + entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); + entry.pushKV("time", pindex->GetBlockTime()); + entry.pushKV("blocktime", pindex->GetBlockTime()); + } chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); } else { entry.pushKV("height", -1); - entry.pushKV("confirmations", 0); + if (detailed) entry.pushKV("confirmations", 0); } } } - bool fLocked = isman.IsLocked(tx.GetHash()); - entry.pushKV("instantlock", fLocked || chainLock); - entry.pushKV("instantlock_internal", fLocked); + if (detailed) { + bool fLocked = isman.IsLocked(tx.GetHash()); + entry.pushKV("instantlock", fLocked || chainLock); + entry.pushKV("instantlock_internal", fLocked); + } entry.pushKV("chainlock", chainLock); } @@ -309,7 +313,8 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) UniValue result_arr(UniValue::VARR); UniValue txids = request.params[0].get_array(); - for (size_t idx = 0; idx < std::min(100, txids.size()); ++idx) { + for (size_t idx = 0; idx < std::min(100, txids.size()); ++idx) { + UniValue result(UniValue::VOBJ); try { const UniValue& txid = txids[idx]; uint256 hash(ParseHashV(txid, "txid")); @@ -337,12 +342,12 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); } - UniValue result(UniValue::VOBJ); - TxLockStatusToUniv(*tx, hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); - result_arr.push_back(result); + TxLockStatusToUniv(*tx, hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result, false); + } catch (const UniValue& error) { - result_arr.push_back(error); + result.pushKV("error", error); } + result_arr.push_back(result); } return result_arr; } From 58d820a804a4230f0b5f9062c2a30bbb3851220a Mon Sep 17 00:00:00 2001 From: pasta Date: Sun, 17 Sep 2023 17:04:13 -0500 Subject: [PATCH 06/14] avoid unneeded error states --- src/rpc/rawtransaction.cpp | 55 +++++++++++++------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0c4cc4c00693..0296e777a404 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -50,17 +50,18 @@ #include -static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry, bool detailed = true) +static void TxLockStatusToUniv(const CTransaction* const tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry, bool detailed = true) { bool chainLock = false; - if (!hashBlock.IsNull()) { + int height = -1; + if (!hashBlock.IsNull() && tx != nullptr) { LOCK(cs_main); - entry.pushKV("blockhash", hashBlock.GetHex()); + if (detailed) entry.pushKV("blockhash", hashBlock.GetHex()); CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); if (pindex) { if (active_chainstate.m_chain.Contains(pindex)) { - entry.pushKV("height", pindex->nHeight); + height = pindex->nHeight; if (detailed) { entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); @@ -68,17 +69,17 @@ static void TxLockStatusToUniv(const CTransaction& tx, const uint256 hashBlock, } chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); } else { - entry.pushKV("height", -1); if (detailed) entry.pushKV("confirmations", 0); } } } if (detailed) { - bool fLocked = isman.IsLocked(tx.GetHash()); + bool fLocked = isman.IsLocked(tx->GetHash()); entry.pushKV("instantlock", fLocked || chainLock); entry.pushKV("instantlock_internal", fLocked); } + entry.pushKV("height", height); entry.pushKV("chainlock", chainLock); } @@ -112,7 +113,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo } TxToUniv(tx, uint256(), entry, true, &txSpentInfo); - TxLockStatusToUniv(tx, hashBlock, active_chainstate, clhandler, isman, entry); + TxLockStatusToUniv(&tx, hashBlock, active_chainstate, clhandler, isman, entry); } static UniValue getrawtransaction(const JSONRPCRequest& request) @@ -302,7 +303,7 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) const NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); - CBlockIndex* blockindex = nullptr; + const CBlockIndex* blockindex = nullptr; bool f_txindex_ready = false; if (g_txindex && !blockindex) { @@ -315,38 +316,18 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) UniValue txids = request.params[0].get_array(); for (size_t idx = 0; idx < std::min(100, txids.size()); ++idx) { UniValue result(UniValue::VOBJ); - try { - const UniValue& txid = txids[idx]; - uint256 hash(ParseHashV(txid, "txid")); - if (hash == Params().GenesisBlock().hashMerkleRoot) { - // Special exception for the genesis block coinbase transaction - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved"); - } + const UniValue& txid = txids[idx]; + uint256 hash(ParseHashV(txid, "txid")); + if (hash == Params().GenesisBlock().hashMerkleRoot) { + // Special exception for the genesis block coinbase transaction + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved"); + } - uint256 hash_block; - const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block); - if (!tx) { - std::string errmsg; - if (blockindex) { - if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) { - throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); - } - errmsg = "No such transaction found in the provided block"; - } else if (!g_txindex) { - errmsg = "No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries"; - } else if (!f_txindex_ready) { - errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed"; - } else { - errmsg = "No such mempool or blockchain transaction"; - } - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); - } + uint256 hash_block; + CTransactionRef tx = GetTransaction(nullptr, nullptr, hash, Params().GetConsensus(), hash_block); - TxLockStatusToUniv(*tx, hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result, false); + TxLockStatusToUniv(tx.get(), hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result, false); - } catch (const UniValue& error) { - result.pushKV("error", error); - } result_arr.push_back(result); } return result_arr; From 74cc26f5a75cbf51b741ff7411bbd8c05fc14b56 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 18 Sep 2023 16:06:21 +0300 Subject: [PATCH 07/14] suggestions --- src/rpc/client.cpp | 2 +- src/rpc/rawtransaction.cpp | 128 ++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 67 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 16fa38738b1a..6b1d251082b9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -107,7 +107,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbose" }, - { "gettransactionsarelocked", 0, "txids" }, + { "getttxchainlocks", 0, "txids" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0296e777a404..645a8fd41774 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -50,39 +50,6 @@ #include -static void TxLockStatusToUniv(const CTransaction* const tx, const uint256 hashBlock, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry, bool detailed = true) -{ - bool chainLock = false; - int height = -1; - if (!hashBlock.IsNull() && tx != nullptr) { - LOCK(cs_main); - - if (detailed) entry.pushKV("blockhash", hashBlock.GetHex()); - CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); - if (pindex) { - if (active_chainstate.m_chain.Contains(pindex)) { - height = pindex->nHeight; - if (detailed) { - entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); - entry.pushKV("time", pindex->GetBlockTime()); - entry.pushKV("blocktime", pindex->GetBlockTime()); - } - chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); - } else { - if (detailed) entry.pushKV("confirmations", 0); - } - } - } - - if (detailed) { - bool fLocked = isman.IsLocked(tx->GetHash()); - entry.pushKV("instantlock", fLocked || chainLock); - entry.pushKV("instantlock_internal", fLocked); - } - entry.pushKV("height", height); - entry.pushKV("chainlock", chainLock); -} - void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempool, CChainState& active_chainstate, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, UniValue& entry) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. @@ -113,7 +80,31 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo } TxToUniv(tx, uint256(), entry, true, &txSpentInfo); - TxLockStatusToUniv(&tx, hashBlock, active_chainstate, clhandler, isman, entry); + + bool chainLock = false; + if (!hashBlock.IsNull()) { + LOCK(cs_main); + + entry.pushKV("blockhash", hashBlock.GetHex()); + CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock); + if (pindex) { + if (active_chainstate.m_chain.Contains(pindex)) { + entry.pushKV("height", pindex->nHeight); + entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); + entry.pushKV("time", pindex->GetBlockTime()); + entry.pushKV("blocktime", pindex->GetBlockTime()); + chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + } else { + entry.pushKV("height", -1); + entry.pushKV("confirmations", 0); + } + } + } + + bool fLocked = isman.IsLocked(txid); + entry.pushKV("instantlock", fLocked || chainLock); + entry.pushKV("instantlock_internal", fLocked); + entry.pushKV("chainlock", chainLock); } static UniValue getrawtransaction(const JSONRPCRequest& request) @@ -269,12 +260,12 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); return result; } -static UniValue gettransactionsarelocked(const JSONRPCRequest& request) + +static UniValue getttxchainlocks(const JSONRPCRequest& request) { RPCHelpMan{ - "gettransactionsarelocked", - "\nReturn the raw transactions data.\n" - "\nHint: Use gettransaction for wallet transactions.\n", + "getttxchainlocks", + "\nReturns a block height each transaction was mined at and whether it is chainlocked or not.\n", { {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction ids (no more than 100)", { @@ -283,51 +274,56 @@ static UniValue gettransactionsarelocked(const JSONRPCRequest& request) }, }, 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", + { + {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "txid", "The transaction hash"}, - {RPCResult::Type::BOOL, "instantlock", "Current transaction lock state"}, - {RPCResult::Type::BOOL, "chainlock", "he state of the corresponding block chainlock"}, - {RPCResult::Type::NUM, "height", "The block height"}, - }}, - } + {RPCResult::Type::NUM, "height", "The block height"}, + {RPCResult::Type::BOOL, "chainlock", "Chainlock status for the block containing the transaction"}, + }}, + } }, RPCExamples{ - HelpExampleCli("gettransactionsarelocked", "\"mytxids\"") - + HelpExampleCli("gettransactionsarelocked", "\"[tx1, tx2, ...]\"") + HelpExampleCli("getttxchainlocks", "'[\"mytxid\",...]'") + + HelpExampleRpc("getttxchainlocks", "[\"mytxid\",...]") }, }.Check(request); const NodeContext& node = EnsureAnyNodeContext(request.context); - ChainstateManager& chainman = EnsureChainman(node); - - const CBlockIndex* blockindex = nullptr; - - bool f_txindex_ready = false; - if (g_txindex && !blockindex) { - f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain(); - } - - LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const ChainstateManager& chainman = EnsureChainman(node); + const CChainState& active_chainstate = chainman.ActiveChainstate(); UniValue result_arr(UniValue::VARR); UniValue txids = request.params[0].get_array(); - for (size_t idx = 0; idx < std::min(100, txids.size()); ++idx) { + if (txids.size() > 100) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only"); + } + + for (size_t idx = 0; idx < txids.size(); ++idx) { UniValue result(UniValue::VOBJ); - const UniValue& txid = txids[idx]; - uint256 hash(ParseHashV(txid, "txid")); - if (hash == Params().GenesisBlock().hashMerkleRoot) { + uint256 txid(ParseHashV(txids[idx], "txid")); + if (txid == Params().GenesisBlock().hashMerkleRoot) { // Special exception for the genesis block coinbase transaction throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved"); } uint256 hash_block; - CTransactionRef tx = GetTransaction(nullptr, nullptr, hash, Params().GetConsensus(), hash_block); + int height = -1; + bool chainLock = false; - TxLockStatusToUniv(tx.get(), hash_block, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result, false); + GetTransaction(nullptr, nullptr, txid, Params().GetConsensus(), hash_block); + if (!hash_block.IsNull()) { + LOCK(cs_main); + CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hash_block); + if (pindex && active_chainstate.m_chain.Contains(pindex)) { + height = pindex->nHeight; + chainLock = llmq_ctx.clhandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + } + } + result.pushKV("height", height); + result.pushKV("chainlock", chainLock); result_arr.push_back(result); } return result_arr; @@ -1755,7 +1751,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "gettransactionsarelocked", &gettransactionsarelocked, {"txids"} }, + { "rawtransactions", "getttxchainlocks", &getttxchainlocks, {"txids"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, From 5b70143960fdb6ec9eb5ea03106723d5ae94570f Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 18 Sep 2023 09:39:24 -0500 Subject: [PATCH 08/14] minor refactoring --- src/rpc/rawtransaction.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 645a8fd41774..4b7032a31dcb 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -300,7 +301,7 @@ static UniValue getttxchainlocks(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only"); } - for (size_t idx = 0; idx < txids.size(); ++idx) { + for (auto idx : irange::range(txids.size())) { UniValue result(UniValue::VOBJ); uint256 txid(ParseHashV(txids[idx], "txid")); if (txid == Params().GenesisBlock().hashMerkleRoot) { @@ -309,14 +310,14 @@ static UniValue getttxchainlocks(const JSONRPCRequest& request) } uint256 hash_block; - int height = -1; - bool chainLock = false; + int height{-1}; + bool chainLock{false}; GetTransaction(nullptr, nullptr, txid, Params().GetConsensus(), hash_block); if (!hash_block.IsNull()) { LOCK(cs_main); - CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hash_block); + const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hash_block); if (pindex && active_chainstate.m_chain.Contains(pindex)) { height = pindex->nHeight; chainLock = llmq_ctx.clhandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash()); From 3208af49ed816b08e684d42820ee94f9523c0fad Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Sep 2023 08:57:49 +0700 Subject: [PATCH 09/14] fix: rename getttxchainlocks to gettxchainlocks --- src/rpc/client.cpp | 2 +- src/rpc/rawtransaction.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 6b1d251082b9..6b686d7ae181 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -107,7 +107,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbose" }, - { "getttxchainlocks", 0, "txids" }, + { "gettxchainlocks", 0, "txids" }, { "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 2, "locktime" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4b7032a31dcb..b6ffc55ff397 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -262,11 +262,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) return result; } -static UniValue getttxchainlocks(const JSONRPCRequest& request) +static UniValue gettxchainlocks(const JSONRPCRequest& request) { RPCHelpMan{ - "getttxchainlocks", - "\nReturns a block height each transaction was mined at and whether it is chainlocked or not.\n", + "gettxchainlocks", + "\nReturns the block height each transaction was mined at and whether it is chainlocked or not.\n", { {"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction ids (no more than 100)", { @@ -285,8 +285,8 @@ static UniValue getttxchainlocks(const JSONRPCRequest& request) } }, RPCExamples{ - HelpExampleCli("getttxchainlocks", "'[\"mytxid\",...]'") - + HelpExampleRpc("getttxchainlocks", "[\"mytxid\",...]") + HelpExampleCli("gettxchainlocks", "'[\"mytxid\",...]'") + + HelpExampleRpc("gettxchainlocks", "[\"mytxid\",...]") }, }.Check(request); @@ -1752,7 +1752,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "getttxchainlocks", &getttxchainlocks, {"txids"} }, + { "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, From cdf6ecda6d40cca7d50c91d26343150602ba577c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Sep 2023 09:19:45 +0700 Subject: [PATCH 10/14] fix: reduce scope of cs_main inside gettxchainlocks --- src/rpc/rawtransaction.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b6ffc55ff397..1f58ac09d8d0 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -320,9 +320,11 @@ static UniValue gettxchainlocks(const JSONRPCRequest& request) const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hash_block); if (pindex && active_chainstate.m_chain.Contains(pindex)) { height = pindex->nHeight; - chainLock = llmq_ctx.clhandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash()); } } + if (height != -1) { + chainLock = llmq_ctx.clhandler->HasChainLock(height, hash_block); + } result.pushKV("height", height); result.pushKV("chainlock", chainLock); result_arr.push_back(result); From 454925df9727dc9fa726b55adf88c77f8ce95f35 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Sep 2023 09:20:47 +0700 Subject: [PATCH 11/14] refactor: adds couple of missing const in gettxchainlocks --- src/rpc/rawtransaction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 1f58ac09d8d0..5f73d3f96f0f 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -301,9 +301,9 @@ static UniValue gettxchainlocks(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only"); } - for (auto idx : irange::range(txids.size())) { + for (const auto idx : irange::range(txids.size())) { UniValue result(UniValue::VOBJ); - uint256 txid(ParseHashV(txids[idx], "txid")); + const uint256 txid(ParseHashV(txids[idx], "txid")); if (txid == Params().GenesisBlock().hashMerkleRoot) { // Special exception for the genesis block coinbase transaction throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved"); From 99a69c4b71b7545bcbc311794690bbdac3719e78 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Sep 2023 09:23:42 +0700 Subject: [PATCH 12/14] feat: adds rpc_tests for gettxchainlocks --- src/test/rpc_tests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 5e7311855868..3eeb868de9b8 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -115,6 +115,10 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), std::runtime_error); + BOOST_CHECK_THROW(CallRPC("gettxchainlocks"), std::runtime_error); + BOOST_CHECK_THROW(CallRPC("gettxchainlocks not_array"), std::runtime_error); + BOOST_CHECK_THROW(CallRPC("gettxchainlocks [] extra"), std::runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error); From d2be2cb0e78ca182d63c2eb4bf187367af9e6392 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 19 Sep 2023 11:04:58 +0700 Subject: [PATCH 13/14] feat: adds functional tests for gettxchainlocks in rpc_verifychainlock.py --- test/functional/rpc_verifychainlock.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/functional/rpc_verifychainlock.py b/test/functional/rpc_verifychainlock.py index fcb31321298a..a11aba3adc17 100755 --- a/test/functional/rpc_verifychainlock.py +++ b/test/functional/rpc_verifychainlock.py @@ -4,12 +4,14 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import DashTestFramework -from test_framework.util import assert_raises_rpc_error +from test_framework.util import assert_equal, assert_raises_rpc_error ''' rpc_verifychainlock.py -Test verifychainlock rpc +Test the following RPC: + - gettxchainlocks + - verifychainlock ''' @@ -57,6 +59,16 @@ def run_test(self): assert node0.verifychainlock(block_hash, chainlock_signature, height) assert node1.verifychainlock(block_hash, chainlock_signature, height) + node1.generate(1) + height1 = node1.getblockcount() + tx0 = node0.getblock(node0.getbestblockhash())['tx'][0] + tx1 = node1.getblock(node1.getbestblockhash())['tx'][0] + locks0 = node0.gettxchainlocks([tx0, tx1]) + locks1 = node1.gettxchainlocks([tx0, tx1]) + unknown_cl_helper = {'height': -1, 'chainlock': False} + assert_equal(locks0, [{'height': height, 'chainlock': True}, unknown_cl_helper]) + assert_equal(locks1, [unknown_cl_helper, {'height': height1, 'chainlock': False}]) + if __name__ == '__main__': RPCVerifyChainLockTest().main() From b1d0467b538afcdf4484ae45be113ee692caa7ba Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 20 Sep 2023 01:28:58 +0700 Subject: [PATCH 14/14] Update src/rpc/rawtransaction.cpp Co-authored-by: UdjinM6 --- src/rpc/rawtransaction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5f73d3f96f0f..15332e71d49a 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -301,6 +301,10 @@ static UniValue gettxchainlocks(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only"); } + if (g_txindex) { + g_txindex->BlockUntilSyncedToCurrentChain(); + } + for (const auto idx : irange::range(txids.size())) { UniValue result(UniValue::VOBJ); const uint256 txid(ParseHashV(txids[idx], "txid"));