Auto merge of #4574 - oxarbitrage:issue4475, r=daira

Add blockhash parameter to getrawtransaction

Code ported manually from https://github.com/bitcoin/bitcoin/pull/10275 in attempt to fix https://github.com/zcash/zcash/issues/4475
This commit is contained in:
Homu 2020-11-10 15:52:11 +00:00
commit 60331b9e83
4 changed files with 114 additions and 53 deletions

View File

@ -11,7 +11,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, initialize_chain_clean, \
start_nodes, connect_nodes_bi
start_nodes, connect_nodes_bi, assert_raises
from decimal import Decimal
@ -68,6 +68,30 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal("Missing inputs" in errorString, True);
#####################################
# getrawtransaction with block hash #
#####################################
# make a tx by sending then generate 2 blocks; block1 has the tx in it
tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
block1, block2 = self.nodes[2].generate(2)
self.sync_all()
# We should be able to get the raw transaction by providing the correct block
gottx = self.nodes[0].getrawtransaction(tx, 1, block1)
assert_equal(gottx['txid'], tx)
assert_equal(gottx['in_active_chain'], True)
# We should not have the 'in_active_chain' flag when we don't provide a block
gottx = self.nodes[0].getrawtransaction(tx, 1)
assert_equal(gottx['txid'], tx)
assert 'in_active_chain' not in gottx
# We should not get the tx if we provide an unrelated block
assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, block2)
# An invalid block hash should raise errors
assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, True)
assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "foobar")
assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "abcd1234")
assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
#########################
# RAW TX MULTISIG TESTS #
#########################

View File

@ -1768,13 +1768,17 @@ bool GetAddressUnspent(const uint160& addressHash, int type,
return true;
}
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &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, CTransaction& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex)
{
CBlockIndex *pindexSlow = NULL;
CBlockIndex* pindexSlow = blockIndex;
LOCK(cs_main);
if (!blockIndex) {
if (mempool.lookup(hash, txOut))
{
return true;
@ -1799,6 +1803,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
return error("%s: txid mismatch", __func__);
return true;
}
// transaction not found in index, nothing more can be done
return false;
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
@ -1812,6 +1819,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
if (nHeight > 0)
pindexSlow = chainActive[nHeight];
}
}
if (pindexSlow) {
CBlock block;
@ -1829,11 +1837,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
return false;
}
//////////////////////////////////////////////////////////////////////////////
//
// CBlock and CBlockIndex

View File

@ -260,7 +260,7 @@ bool IsInitialBlockDownload(const CChainParams& chainParams);
/** Pair of timestamp and formatted string that describes several potential problems detected by the core */
std::pair<std::string, int64_t> GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
bool GetTransaction(const uint256& hash, CTransaction& 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 */
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);

View File

@ -268,25 +268,30 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
UniValue getrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
"getrawtransaction \"txid\" ( verbose )\n"
"\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n"
"or there is an unspent output in the utxo for this transaction. To make it always work,\n"
"you need to maintain a transaction index, using the -txindex command line option.\n"
"getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
"\nNOTE: If \"blockhash\" is not provided and the -txindex option is not enabled, then this call only\n"
"works for mempool transactions. If either \"blockhash\" is provided or the -txindex option is\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"
"\nReturn the raw transaction data.\n"
"\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n"
"If verbose is non-zero, returns an Object with information about 'txid'.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n"
"2. verbose (numeric, optional, default=0) If 0, return a string of hex-encoded data, 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 0):\n"
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
"\nResult (if verbose > 0):\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"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"size\" : n, (numeric) The transaction size\n"
@ -359,20 +364,48 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" 1")
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" 0 \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" 1 \"myblockhash\"")
);
LOCK(cs_main);
bool in_active_chain = true;
uint256 hash = ParseHashV(params[0], "parameter 1");
CBlockIndex* blockindex = nullptr;
bool fVerbose = false;
if (params.size() > 1)
fVerbose = (params[1].get_int() != 0);
LOCK(cs_main);
if (params.size() > 2) {
uint256 blockhash = ParseHashV(params[2], "parameter 3");
if (!blockhash.IsNull()) {
BlockMap::iterator it = mapBlockIndex.find(blockhash);
if (it == mapBlockIndex.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
blockindex = it->second;
in_active_chain = chainActive.Contains(blockindex);
}
}
CTransaction tx;
uint256 hashBlock;
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
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.");
}
string strHex = EncodeHexTx(tx);
@ -380,8 +413,9 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
return strHex;
UniValue result(UniValue::VOBJ);
if (blockindex) result.pushKV("in_active_chain", in_active_chain);
result.pushKV("hex", strHex);
TxToJSON(tx, hashBlock, result);
TxToJSON(tx, hash_block, result);
return result;
}