diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -129,9 +129,9 @@ static UniValue getrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || - request.params.size() > 2) { + request.params.size() > 3) { throw std::runtime_error( - "getrawtransaction \"txid\" ( verbose )\n" + "getrawtransaction \"txid\" ( verbose ( \"blockid\" ) )\n" "\nNOTE: By default this function only works for mempool " "transactions. If the -txindex option is\n" @@ -149,6 +149,8 @@ "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" + "3. \"blockid\" (string, optional) The block id; allows " + "retrieval when run without -txindex option (e.g. pruning enabled)" "\nResult (if verbose is not set or set to false):\n" "\"data\" (string) The serialized, hex-encoded data for " @@ -209,6 +211,8 @@ "\nExamples:\n" + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + + HelpExampleCli("getrawtransaction", + "\"mytxid\" true \"myblockid\"") + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")); } @@ -234,14 +238,27 @@ } } + CBlockIndex *blockindex = nullptr; + if (request.params.size() > 2) { + uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); + + BlockMap::iterator it = mapBlockIndex.find(blockhash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block hash not found"); + } + blockindex = it->second; + } + CTransactionRef tx; uint256 hashBlock; - if (!GetTransaction(config, txid, tx, hashBlock, true)) { + if (!GetTransaction(config, txid, tx, hashBlock, true, blockindex)) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" : "No such mempool transaction. Use -txindex " - "to enable blockchain transaction queries") + + "to enable blockchain transaction queries, " + "or specify the containing blockid") + ". Use gettransaction for wallet transactions."); } diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -396,7 +396,8 @@ * Retrieve a transaction (from memory pool, or from disk, if possible). */ bool GetTransaction(const Config &config, const TxId &txid, CTransactionRef &tx, - uint256 &hashBlock, bool fAllowSlow = false); + uint256 &hashBlock, bool fAllowSlow = false, + CBlockIndex *blockIndex = nullptr); /** * Find the best known block, and make it the active tip of the block chain. diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -606,10 +606,10 @@ // . Defaults to the pre-defined timestamp when not set. static bool IsReplayProtectionEnabled(const Config &config, int64_t nMedianTimePast) { - return nMedianTimePast >= gArgs.GetArg("-replayprotectionactivationtime", - config.GetChainParams() - .GetConsensus() - .greatWallActivationTime); + return nMedianTimePast >= + gArgs.GetArg( + "-replayprotectionactivationtime", + config.GetChainParams().GetConsensus().greatWallActivationTime); } static bool IsReplayProtectionEnabled(const Config &config, @@ -1115,54 +1115,60 @@ /** * Return transaction in txOut, and if it was found inside a block, its hash is - * placed in hashBlock. + * placed in hashBlock. If blockIndex is provided, mempool is skipped and the + * transaction is read from disk (and fAllowSlow has no effect; disk read is + * forced). */ bool GetTransaction(const Config &config, const TxId &txid, - CTransactionRef &txOut, uint256 &hashBlock, - bool fAllowSlow) { - CBlockIndex *pindexSlow = nullptr; + CTransactionRef &txOut, uint256 &hashBlock, bool fAllowSlow, + CBlockIndex *blockIndex) { + CBlockIndex *pindexSlow = blockIndex; LOCK(cs_main); - CTransactionRef ptx = mempool.get(txid); - if (ptx) { - txOut = ptx; - return true; - } + // skip mempool checks if blockIndex was provided (block already known) + if (!blockIndex) { + CTransactionRef ptx = mempool.get(txid); + if (ptx) { + txOut = ptx; + return true; + } - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(txid, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, - CLIENT_VERSION); - if (file.IsNull()) { - return error("%s: OpenBlockFile failed", __func__); - } + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(txid, 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()); - } + 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->GetId() != txid) { - return error("%s: txid mismatch", __func__); - } + hashBlock = header.GetHash(); + if (txOut->GetId() != txid) { + return error("%s: txid mismatch", __func__); + } - return true; + return true; + } } - } - // use coin database to locate block that contains transaction, and scan it - if (fAllowSlow) { - const Coin &coin = AccessByTxid(*pcoinsTip, txid); - if (!coin.IsSpent()) { - pindexSlow = chainActive[coin.GetHeight()]; + // use coin database to locate block that contains transaction, and scan + // it + if (fAllowSlow) { + const Coin &coin = AccessByTxid(*pcoinsTip, txid); + if (!coin.IsSpent()) { + pindexSlow = chainActive[coin.GetHeight()]; + } } }