listsinceblock: optionally find and list any transactions that were undone due to reorg when requesting a non-main chain block in a new 'removed' array.
This commit is contained in:
parent
6adc3a3732
commit
f999c46cae
2 changed files with 60 additions and 21 deletions
|
@ -68,6 +68,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "getblocktemplate", 0, "template_request" },
|
||||
{ "listsinceblock", 1, "target_confirmations" },
|
||||
{ "listsinceblock", 2, "include_watchonly" },
|
||||
{ "listsinceblock", 3, "include_removed" },
|
||||
{ "sendmany", 1, "amounts" },
|
||||
{ "sendmany", 2, "minconf" },
|
||||
{ "sendmany", 4, "subtractfeefrom" },
|
||||
|
|
|
@ -1426,6 +1426,17 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
|
|||
entry.push_back(Pair("address", addr.ToString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* List transactions based on the given criteria.
|
||||
*
|
||||
* @param pwallet The wallet.
|
||||
* @param wtx The wallet transaction.
|
||||
* @param strAccount The account, if any, or "*" for all.
|
||||
* @param nMinDepth The minimum confirmation depth.
|
||||
* @param fLong Whether to include the JSON version of the transaction.
|
||||
* @param ret The UniValue into which the result is stored.
|
||||
* @param filter The "is mine" filter bool.
|
||||
*/
|
||||
void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
|
||||
{
|
||||
CAmount nFee;
|
||||
|
@ -1742,14 +1753,18 @@ UniValue listsinceblock(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() > 3)
|
||||
if (request.fHelp || request.params.size() > 4)
|
||||
throw std::runtime_error(
|
||||
"listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n"
|
||||
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
|
||||
"listsinceblock ( \"blockhash\" target_confirmations include_watchonly include_removed )\n"
|
||||
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
|
||||
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
|
||||
"Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
|
||||
"2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
|
||||
"3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')"
|
||||
"2. target_confirmations: (numeric, optional, default=1) The confirmations required, must be 1 or more\n"
|
||||
"3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
|
||||
"4. include_removed: (bool, optional, default=true) Show transactions that were removed due to a reorg in the \"removed\" array\n"
|
||||
" (not guaranteed to work on pruned nodes)\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"transactions\": [\n"
|
||||
|
@ -1775,6 +1790,10 @@ UniValue listsinceblock(const JSONRPCRequest& request)
|
|||
" \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
|
||||
" \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
|
||||
" ],\n"
|
||||
" \"removed\": [\n"
|
||||
" <structure is the same as \"transactions\" above, only present if include_removed=true>\n"
|
||||
" Note: transactions that were readded in the active chain will appear as-is in this array, and may thus have a positive confirmation count.\n"
|
||||
" ],\n"
|
||||
" \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
|
@ -1785,21 +1804,19 @@ UniValue listsinceblock(const JSONRPCRequest& request)
|
|||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
const CBlockIndex *pindex = NULL;
|
||||
const CBlockIndex* pindex = NULL; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
|
||||
const CBlockIndex* paltindex = NULL; // Block index of the specified block, even if it's in a deactivated chain.
|
||||
int target_confirms = 1;
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
if (!request.params[0].isNull())
|
||||
{
|
||||
if (!request.params[0].isNull()) {
|
||||
uint256 blockId;
|
||||
|
||||
blockId.SetHex(request.params[0].get_str());
|
||||
BlockMap::iterator it = mapBlockIndex.find(blockId);
|
||||
if (it != mapBlockIndex.end())
|
||||
{
|
||||
pindex = it->second;
|
||||
if (chainActive[pindex->nHeight] != pindex)
|
||||
{
|
||||
if (it != mapBlockIndex.end()) {
|
||||
paltindex = pindex = it->second;
|
||||
if (chainActive[pindex->nHeight] != pindex) {
|
||||
// the block being asked for is a part of a deactivated chain;
|
||||
// we don't want to depend on its perceived height in the block
|
||||
// chain, we want to instead use the last common ancestor
|
||||
|
@ -1808,19 +1825,20 @@ UniValue listsinceblock(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
if (!request.params[1].isNull())
|
||||
{
|
||||
if (!request.params[1].isNull()) {
|
||||
target_confirms = request.params[1].get_int();
|
||||
|
||||
if (target_confirms < 1)
|
||||
if (target_confirms < 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.params.size() > 2 && request.params[2].get_bool())
|
||||
{
|
||||
if (!request.params[2].isNull() && request.params[2].get_bool()) {
|
||||
filter = filter | ISMINE_WATCH_ONLY;
|
||||
}
|
||||
|
||||
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
|
||||
|
||||
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
|
||||
|
||||
UniValue transactions(UniValue::VARR);
|
||||
|
@ -1828,15 +1846,35 @@ UniValue listsinceblock(const JSONRPCRequest& request)
|
|||
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
||||
CWalletTx tx = pairWtx.second;
|
||||
|
||||
if (depth == -1 || tx.GetDepthInMainChain() < depth)
|
||||
if (depth == -1 || tx.GetDepthInMainChain() < depth) {
|
||||
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter);
|
||||
}
|
||||
}
|
||||
|
||||
// when a reorg'd block is requested, we also list any relevant transactions
|
||||
// in the blocks of the chain that was detached
|
||||
UniValue removed(UniValue::VARR);
|
||||
while (include_removed && paltindex && paltindex != pindex) {
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, paltindex, Params().GetConsensus())) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
||||
}
|
||||
for (const CTransactionRef& tx : block.vtx) {
|
||||
if (pwallet->mapWallet.count(tx->GetHash()) > 0) {
|
||||
// We want all transactions regardless of confirmation count to appear here,
|
||||
// even negative confirmation ones, hence the big negative.
|
||||
ListTransactions(pwallet, pwallet->mapWallet[tx->GetHash()], "*", -100000000, true, removed, filter);
|
||||
}
|
||||
}
|
||||
paltindex = paltindex->pprev;
|
||||
}
|
||||
|
||||
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
|
||||
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("transactions", transactions));
|
||||
if (include_removed) ret.push_back(Pair("removed", removed));
|
||||
ret.push_back(Pair("lastblock", lastblock.GetHex()));
|
||||
|
||||
return ret;
|
||||
|
@ -3082,7 +3120,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "listlockunspent", &listlockunspent, false, {} },
|
||||
{ "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} },
|
||||
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
|
||||
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
|
||||
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
|
||||
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
|
||||
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
|
||||
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
|
||||
|
|
Loading…
Reference in a new issue