diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 8186612bdd964..618d955d7a780 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -9,6 +9,7 @@ #include "chainparams.h" #include "checkpoints.h" #include "coins.h" +#include "core_io.h" #include "consensus/validation.h" #include "instantx.h" #include "validation.h" @@ -1633,6 +1634,113 @@ UniValue reconsiderblock(const JSONRPCRequest& request) return NullUniValue; } +UniValue getspecialtxes(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 5) + throw std::runtime_error( + "getspecialtxes \"blockhash\" ( type count skip verbosity ) \n" + "Returns an array of special transactions found in the specified block\n" + "\nIf verbosity is 0, returns tx hash for each transaction.\n" + "If verbosity is 1, returns hex-encoded data for each transaction.\n" + "If verbosity is 2, returns an Object with information for each transaction.\n" + "\nArguments:\n" + "1. \"blockhash\" (string, required) The block hash\n" + "2. type (numeric, optional, default=-1) Filter special txes by type, -1 means all types\n" + "3. count (numeric, optional, default=10) The number of transactions to return\n" + "4. skip (numeric, optional, default=0) The number of transactions to skip\n" + "5. verbosity (numeric, optional, default=0) 0 for hashes, 1 for hex-encoded data, and 2 for json object\n" + "\nResult (for verbosity = 0):\n" + "[\n" + " \"txid\" : \"xxxx\", (string) The transaction id\n" + "]\n" + "\nResult (for verbosity = 1):\n" + "[\n" + " \"data\", (string) A string that is serialized, hex-encoded data for the transaction\n" + "]\n" + "\nResult (for verbosity = 2):\n" + "[ (array of Objects) The transactions in the format of the getrawtransaction RPC.\n" + " ...,\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") + + HelpExampleRpc("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") + ); + + LOCK(cs_main); + + std::string strHash = request.params[0].get_str(); + uint256 hash(uint256S(strHash)); + + int nTxType = -1; + if (request.params.size() > 1) { + nTxType = request.params[1].get_int(); + } + + int nCount = 10; + if (request.params.size() > 2) { + nCount = request.params[2].get_int(); + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + } + + int nSkip = 0; + if (request.params.size() > 3) { + nSkip = request.params[3].get_int(); + if (nSkip < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip"); + } + + int nVerbosity = 0; + if (request.params.size() > 4) { + nVerbosity = request.params[4].get_int(); + if (nVerbosity < 0 || nVerbosity > 2) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2"); + } + } + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + int nTxNum = 0; + UniValue result(UniValue::VARR); + for(const auto& tx : block.vtx) + { + if (tx->nVersion != 3 || tx->nType == TRANSACTION_NORMAL // ensure it's in fact a special tx + || (nTxType != -1 && tx->nType != nTxType)) { // ensure special tx type matches filter, if given + continue; + } + + nTxNum++; + if (nTxNum <= nSkip) continue; + if (nTxNum > nSkip + nCount) break; + + switch (nVerbosity) + { + case 0 : result.push_back(tx->GetHash().GetHex()); break; + case 1 : result.push_back(EncodeHexTx(*tx)); break; + case 2 : + { + UniValue objTx(UniValue::VOBJ); + TxToJSON(*tx, uint256(), objTx); + result.push_back(objTx); + break; + } + default : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unsupported verbosity"); + } + } + + return result; +} + static const CRPCCommand commands[] = { // category name actor (function) okSafe argNames // --------------------- ------------------------ ----------------------- ------ ---------- @@ -1651,6 +1759,7 @@ static const CRPCCommand commands[] = { "blockchain", "getmempoolentry", &getmempoolentry, true, {"txid"} }, { "blockchain", "getmempoolinfo", &getmempoolinfo, true, {} }, { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} }, + { "blockchain", "getspecialtxes", &getspecialtxes, true, {"blockhash", "type", "count", "skip", "verbosity"} }, { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} }, { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 97530b0f1ce08..9905345d0c153 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -154,6 +154,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getaddressdeltas", 0, "addresses" }, { "getaddressutxos", 0, "addresses" }, { "getaddressmempool", 0, "addresses" }, + { "getspecialtxes", 1, "type" }, + { "getspecialtxes", 2, "count" }, + { "getspecialtxes", 3, "skip" }, + { "getspecialtxes", 4, "verbosity" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" },