Skip to content

Commit f999c46

Browse files
committed
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.
1 parent 6adc3a3 commit f999c46

File tree

2 files changed

+60
-21
lines changed

2 files changed

+60
-21
lines changed

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
6868
{ "getblocktemplate", 0, "template_request" },
6969
{ "listsinceblock", 1, "target_confirmations" },
7070
{ "listsinceblock", 2, "include_watchonly" },
71+
{ "listsinceblock", 3, "include_removed" },
7172
{ "sendmany", 1, "amounts" },
7273
{ "sendmany", 2, "minconf" },
7374
{ "sendmany", 4, "subtractfeefrom" },

src/wallet/rpcwallet.cpp

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,17 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
14261426
entry.push_back(Pair("address", addr.ToString()));
14271427
}
14281428

1429+
/**
1430+
* List transactions based on the given criteria.
1431+
*
1432+
* @param pwallet The wallet.
1433+
* @param wtx The wallet transaction.
1434+
* @param strAccount The account, if any, or "*" for all.
1435+
* @param nMinDepth The minimum confirmation depth.
1436+
* @param fLong Whether to include the JSON version of the transaction.
1437+
* @param ret The UniValue into which the result is stored.
1438+
* @param filter The "is mine" filter bool.
1439+
*/
14291440
void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
14301441
{
14311442
CAmount nFee;
@@ -1742,14 +1753,18 @@ UniValue listsinceblock(const JSONRPCRequest& request)
17421753
return NullUniValue;
17431754
}
17441755

1745-
if (request.fHelp || request.params.size() > 3)
1756+
if (request.fHelp || request.params.size() > 4)
17461757
throw std::runtime_error(
1747-
"listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n"
1748-
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
1758+
"listsinceblock ( \"blockhash\" target_confirmations include_watchonly include_removed )\n"
1759+
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
1760+
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
1761+
"Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n"
17491762
"\nArguments:\n"
17501763
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
1751-
"2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
1752-
"3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')"
1764+
"2. target_confirmations: (numeric, optional, default=1) The confirmations required, must be 1 or more\n"
1765+
"3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
1766+
"4. include_removed: (bool, optional, default=true) Show transactions that were removed due to a reorg in the \"removed\" array\n"
1767+
" (not guaranteed to work on pruned nodes)\n"
17531768
"\nResult:\n"
17541769
"{\n"
17551770
" \"transactions\": [\n"
@@ -1774,7 +1789,11 @@ UniValue listsinceblock(const JSONRPCRequest& request)
17741789
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
17751790
" \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
17761791
" \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
1777-
" ],\n"
1792+
" ],\n"
1793+
" \"removed\": [\n"
1794+
" <structure is the same as \"transactions\" above, only present if include_removed=true>\n"
1795+
" 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"
1796+
" ],\n"
17781797
" \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
17791798
"}\n"
17801799
"\nExamples:\n"
@@ -1785,21 +1804,19 @@ UniValue listsinceblock(const JSONRPCRequest& request)
17851804

17861805
LOCK2(cs_main, pwallet->cs_wallet);
17871806

1788-
const CBlockIndex *pindex = NULL;
1807+
const CBlockIndex* pindex = NULL; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
1808+
const CBlockIndex* paltindex = NULL; // Block index of the specified block, even if it's in a deactivated chain.
17891809
int target_confirms = 1;
17901810
isminefilter filter = ISMINE_SPENDABLE;
17911811

1792-
if (!request.params[0].isNull())
1793-
{
1812+
if (!request.params[0].isNull()) {
17941813
uint256 blockId;
17951814

17961815
blockId.SetHex(request.params[0].get_str());
17971816
BlockMap::iterator it = mapBlockIndex.find(blockId);
1798-
if (it != mapBlockIndex.end())
1799-
{
1800-
pindex = it->second;
1801-
if (chainActive[pindex->nHeight] != pindex)
1802-
{
1817+
if (it != mapBlockIndex.end()) {
1818+
paltindex = pindex = it->second;
1819+
if (chainActive[pindex->nHeight] != pindex) {
18031820
// the block being asked for is a part of a deactivated chain;
18041821
// we don't want to depend on its perceived height in the block
18051822
// chain, we want to instead use the last common ancestor
@@ -1808,35 +1825,56 @@ UniValue listsinceblock(const JSONRPCRequest& request)
18081825
}
18091826
}
18101827

1811-
if (!request.params[1].isNull())
1812-
{
1828+
if (!request.params[1].isNull()) {
18131829
target_confirms = request.params[1].get_int();
18141830

1815-
if (target_confirms < 1)
1831+
if (target_confirms < 1) {
18161832
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1833+
}
18171834
}
18181835

1819-
if (request.params.size() > 2 && request.params[2].get_bool())
1820-
{
1836+
if (!request.params[2].isNull() && request.params[2].get_bool()) {
18211837
filter = filter | ISMINE_WATCH_ONLY;
18221838
}
18231839

1840+
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
1841+
18241842
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
18251843

18261844
UniValue transactions(UniValue::VARR);
18271845

18281846
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
18291847
CWalletTx tx = pairWtx.second;
18301848

1831-
if (depth == -1 || tx.GetDepthInMainChain() < depth)
1849+
if (depth == -1 || tx.GetDepthInMainChain() < depth) {
18321850
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter);
1851+
}
1852+
}
1853+
1854+
// when a reorg'd block is requested, we also list any relevant transactions
1855+
// in the blocks of the chain that was detached
1856+
UniValue removed(UniValue::VARR);
1857+
while (include_removed && paltindex && paltindex != pindex) {
1858+
CBlock block;
1859+
if (!ReadBlockFromDisk(block, paltindex, Params().GetConsensus())) {
1860+
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
1861+
}
1862+
for (const CTransactionRef& tx : block.vtx) {
1863+
if (pwallet->mapWallet.count(tx->GetHash()) > 0) {
1864+
// We want all transactions regardless of confirmation count to appear here,
1865+
// even negative confirmation ones, hence the big negative.
1866+
ListTransactions(pwallet, pwallet->mapWallet[tx->GetHash()], "*", -100000000, true, removed, filter);
1867+
}
1868+
}
1869+
paltindex = paltindex->pprev;
18331870
}
18341871

18351872
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
18361873
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
18371874

18381875
UniValue ret(UniValue::VOBJ);
18391876
ret.push_back(Pair("transactions", transactions));
1877+
if (include_removed) ret.push_back(Pair("removed", removed));
18401878
ret.push_back(Pair("lastblock", lastblock.GetHex()));
18411879

18421880
return ret;
@@ -3082,7 +3120,7 @@ static const CRPCCommand commands[] =
30823120
{ "wallet", "listlockunspent", &listlockunspent, false, {} },
30833121
{ "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} },
30843122
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
3085-
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
3123+
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
30863124
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
30873125
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
30883126
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },

0 commit comments

Comments
 (0)