@@ -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+ */
14291440void 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- " \n Get all transactions in blocks since block [blockhash], or all transactions if omitted\n "
1758+ " listsinceblock ( \" blockhash\" target_confirmations include_watchonly include_removed )\n "
1759+ " \n Get 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 " \n Arguments:\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 " \n Result:\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 " \n Examples:\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