[rpc] Allow getrawtransaction to take optional blockhash to fetch transaction from a block directly.
This commit is contained in:
parent
a5f5a2ce53
commit
b167951677
3 changed files with 84 additions and 47 deletions
|
@ -64,12 +64,15 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||||
|
|
||||||
UniValue getrawtransaction(const JSONRPCRequest& request)
|
UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"getrawtransaction \"txid\" ( verbose )\n"
|
"getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
|
||||||
|
|
||||||
"\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
|
"\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
|
||||||
"enabled, it also works for blockchain transactions.\n"
|
"enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
|
||||||
|
"is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
|
||||||
|
"provided, only that block will be searched and if the transaction is in the mempool or other\n"
|
||||||
|
"blocks, or if this node does not have the given block available, the transaction will not be found.\n"
|
||||||
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
|
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
|
||||||
|
|
||||||
"\nReturn the raw transaction data.\n"
|
"\nReturn the raw transaction data.\n"
|
||||||
|
@ -78,13 +81,15 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||||
|
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"txid\" (string, required) The transaction id\n"
|
"1. \"txid\" (string, required) The transaction id\n"
|
||||||
"2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
|
"2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n"
|
||||||
|
"3. \"blockhash\" (string, optional) The block in which to look for the transaction\n"
|
||||||
|
|
||||||
"\nResult (if verbose is not set or set to false):\n"
|
"\nResult (if verbose is not set or set to false):\n"
|
||||||
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
|
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
|
||||||
|
|
||||||
"\nResult (if verbose is set to true):\n"
|
"\nResult (if verbose is set to true):\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
" \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
|
||||||
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
|
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
|
||||||
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
|
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
|
||||||
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
|
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
|
||||||
|
@ -132,11 +137,15 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||||
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
|
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
|
||||||
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true")
|
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true")
|
||||||
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
|
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
|
||||||
|
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
|
||||||
|
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
|
||||||
);
|
);
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
bool in_active_chain = true;
|
||||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||||
|
CBlockIndex* blockindex = nullptr;
|
||||||
|
|
||||||
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
|
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
|
||||||
bool fVerbose = false;
|
bool fVerbose = false;
|
||||||
|
@ -144,18 +153,42 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||||
fVerbose = request.params[1].isNum() ? (request.params[1].get_int() != 0) : request.params[1].get_bool();
|
fVerbose = request.params[1].isNum() ? (request.params[1].get_int() != 0) : request.params[1].get_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef tx;
|
if (!request.params[2].isNull()) {
|
||||||
uint256 hashBlock;
|
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
|
||||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
|
if (!blockhash.IsNull()) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction"
|
BlockMap::iterator it = mapBlockIndex.find(blockhash);
|
||||||
: "No such mempool transaction. Use -txindex to enable blockchain transaction queries") +
|
if (it == mapBlockIndex.end()) {
|
||||||
". Use gettransaction for wallet transactions.");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
|
||||||
|
}
|
||||||
|
blockindex = it->second;
|
||||||
|
in_active_chain = chainActive.Contains(blockindex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!fVerbose)
|
CTransactionRef tx;
|
||||||
|
uint256 hash_block;
|
||||||
|
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) {
|
||||||
|
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 {
|
||||||
|
errmsg = fTxIndex
|
||||||
|
? "No such mempool or blockchain transaction"
|
||||||
|
: "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
|
||||||
|
}
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fVerbose) {
|
||||||
return EncodeHexTx(*tx, RPCSerializationFlags());
|
return EncodeHexTx(*tx, RPCSerializationFlags());
|
||||||
|
}
|
||||||
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
TxToJSON(*tx, hashBlock, result);
|
if (blockindex) result.push_back(Pair("in_active_chain", in_active_chain));
|
||||||
|
TxToJSON(*tx, hash_block, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -983,7 +1016,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
// --------------------- ------------------------ ----------------------- ----------
|
// --------------------- ------------------------ ----------------------- ----------
|
||||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} },
|
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
|
||||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
|
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
|
||||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} },
|
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} },
|
||||||
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
|
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
|
||||||
|
|
|
@ -926,47 +926,51 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee);
|
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
|
/**
|
||||||
bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
|
* Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
|
||||||
|
* If blockIndex is provided, the transaction is fetched from the corresponding block.
|
||||||
|
*/
|
||||||
|
bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex)
|
||||||
{
|
{
|
||||||
CBlockIndex *pindexSlow = nullptr;
|
CBlockIndex* pindexSlow = blockIndex;
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
CTransactionRef ptx = mempool.get(hash);
|
if (!blockIndex) {
|
||||||
if (ptx)
|
CTransactionRef ptx = mempool.get(hash);
|
||||||
{
|
if (ptx) {
|
||||||
txOut = ptx;
|
txOut = ptx;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fTxIndex) {
|
|
||||||
CDiskTxPos postx;
|
|
||||||
if (pblocktree->ReadTxIndex(hash, postx)) {
|
|
||||||
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
||||||
if (file.IsNull())
|
|
||||||
return error("%s: OpenBlockFile failed", __func__);
|
|
||||||
CBlockHeader header;
|
|
||||||
try {
|
|
||||||
file >> header;
|
|
||||||
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
||||||
file >> txOut;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
|
||||||
}
|
|
||||||
hashBlock = header.GetHash();
|
|
||||||
if (txOut->GetHash() != hash)
|
|
||||||
return error("%s: txid mismatch", __func__);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction not found in index, nothing more can be done
|
if (fTxIndex) {
|
||||||
return false;
|
CDiskTxPos postx;
|
||||||
}
|
if (pblocktree->ReadTxIndex(hash, postx)) {
|
||||||
|
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
||||||
|
if (file.IsNull())
|
||||||
|
return error("%s: OpenBlockFile failed", __func__);
|
||||||
|
CBlockHeader header;
|
||||||
|
try {
|
||||||
|
file >> header;
|
||||||
|
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
||||||
|
file >> txOut;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||||
|
}
|
||||||
|
hashBlock = header.GetHash();
|
||||||
|
if (txOut->GetHash() != hash)
|
||||||
|
return error("%s: txid mismatch", __func__);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
// transaction not found in index, nothing more can be done
|
||||||
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
return false;
|
||||||
if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
|
}
|
||||||
|
|
||||||
|
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||||
|
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
||||||
|
if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pindexSlow) {
|
if (pindexSlow) {
|
||||||
|
|
|
@ -273,7 +273,7 @@ void ThreadScriptCheck();
|
||||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||||
bool IsInitialBlockDownload();
|
bool IsInitialBlockDownload();
|
||||||
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
||||||
bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
|
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, bool fAllowSlow = false, CBlockIndex* blockIndex = nullptr);
|
||||||
/** Find the best known block, and make it the tip of the block chain */
|
/** Find the best known block, and make it the tip of the block chain */
|
||||||
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
|
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
|
||||||
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
|
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
|
||||||
|
|
Loading…
Reference in a new issue