feat: new rpc `gettxchainlocks' to get transaction statuses by batch (#5578)

## Issue being fixed or feature implemented
Requested by @QuantumExplorer for platform needs

## What was done?
New rpc `gettransactionsarelocked` that returns list of txes.
it does less heavy calculations and transfer less data by gRPC.


## How Has This Been Tested?
```
$ src/dash-cli gettransactionsarelocked  '["e469de7994b9c1da8efd262fee8843efd7bdcab80c700dc1059c98b28f7c5c1b", "0d9fdf00c9568ff9103742b64e6b8287794633072f8824fa2c475f59e71dbace","0d3f48eebead54d640a7fc5692ddfcba619d8b49347d9a7c04586057c02dec9f"]'

[
  {
    "height": 907801,
    "chainlock": true
  },
  {
    "height": 101,
    "chainlock": true
  },
  {
    "height": -1,
    "chainlock": false
  }
]
```
Limiter tested by this call:
```
src/dash-cli gettransactionsarelocked  '["", "","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""]'  | wc
```

## 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

---------

Co-authored-by: pasta <pasta@dashboost.org>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
Konstantin Akimov 2023-09-20 21:07:24 +07:00 committed by GitHub
parent 400d171d04
commit 633cc3260f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 2 deletions

View File

@ -107,6 +107,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettransaction", 1, "include_watchonly" }, { "gettransaction", 1, "include_watchonly" },
{ "gettransaction", 2, "verbose" }, { "gettransaction", 2, "verbose" },
{ "getrawtransaction", 1, "verbose" }, { "getrawtransaction", 1, "verbose" },
{ "gettxchainlocks", 0, "txids" },
{ "createrawtransaction", 0, "inputs" }, { "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 1, "outputs" },
{ "createrawtransaction", 2, "locktime" }, { "createrawtransaction", 2, "locktime" },

View File

@ -38,6 +38,7 @@
#include <util/string.h> #include <util/string.h>
#include <validation.h> #include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <util/irange.h>
#include <evo/specialtx.h> #include <evo/specialtx.h>
@ -261,6 +262,80 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
return result; return result;
} }
static UniValue gettxchainlocks(const JSONRPCRequest& request)
{
RPCHelpMan{
"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)",
{
{"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::NUM, "height", "The block height"},
{RPCResult::Type::BOOL, "chainlock", "Chainlock status for the block containing the transaction"},
}},
}
},
RPCExamples{
HelpExampleCli("gettxchainlocks", "'[\"mytxid\",...]'")
+ HelpExampleRpc("gettxchainlocks", "[\"mytxid\",...]")
},
}.Check(request);
const NodeContext& node = EnsureAnyNodeContext(request.context);
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();
if (txids.size() > 100) {
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"));
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;
int height{-1};
bool chainLock{false};
GetTransaction(nullptr, nullptr, txid, Params().GetConsensus(), hash_block);
if (!hash_block.IsNull()) {
LOCK(cs_main);
const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hash_block);
if (pindex && active_chainstate.m_chain.Contains(pindex)) {
height = pindex->nHeight;
}
}
if (height != -1) {
chainLock = llmq_ctx.clhandler->HasChainLock(height, hash_block);
}
result.pushKV("height", height);
result.pushKV("chainlock", chainLock);
result_arr.push_back(result);
}
return result_arr;
}
static UniValue gettxoutproof(const JSONRPCRequest& request) static UniValue gettxoutproof(const JSONRPCRequest& request)
{ {
RPCHelpMan{"gettxoutproof", RPCHelpMan{"gettxoutproof",
@ -1683,6 +1758,7 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },

View File

@ -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 not_hex"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), 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"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error);

View File

@ -4,12 +4,14 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import DashTestFramework 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 rpc_verifychainlock.py
Test verifychainlock rpc Test the following RPC:
- gettxchainlocks
- verifychainlock
''' '''
@ -57,6 +59,16 @@ class RPCVerifyChainLockTest(DashTestFramework):
assert node0.verifychainlock(block_hash, chainlock_signature, height) assert node0.verifychainlock(block_hash, chainlock_signature, height)
assert node1.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__': if __name__ == '__main__':
RPCVerifyChainLockTest().main() RPCVerifyChainLockTest().main()