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:
commit
60331b9e83
|
@ -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 #
|
||||
#########################
|
||||
|
|
83
src/main.cpp
83
src/main.cpp
|
@ -1768,49 +1768,57 @@ 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 (mempool.lookup(hash, txOut))
|
||||
{
|
||||
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__);
|
||||
if (!blockIndex) {
|
||||
if (mempool.lookup(hash, txOut))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||
int nHeight = -1;
|
||||
{
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
const CCoins* coins = view.AccessCoins(hash);
|
||||
if (coins)
|
||||
nHeight = coins->nHeight;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
int nHeight = -1;
|
||||
{
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
const CCoins* coins = view.AccessCoins(hash);
|
||||
if (coins)
|
||||
nHeight = coins->nHeight;
|
||||
}
|
||||
if (nHeight > 0)
|
||||
pindexSlow = chainActive[nHeight];
|
||||
}
|
||||
if (nHeight > 0)
|
||||
pindexSlow = chainActive[nHeight];
|
||||
}
|
||||
|
||||
if (pindexSlow) {
|
||||
|
@ -1829,11 +1837,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CBlock and CBlockIndex
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue