Bitcoin Core 32.99.0
P2P Digital Currency
transactions.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <core_io.h>
6#include <key_io.h>
7#include <policy/rbf.h>
9#include <rpc/util.h>
11#include <rpc/blockchain.h>
12#include <util/vector.h>
13#include <wallet/receive.h>
14#include <wallet/rpc/util.h>
15#include <wallet/scan.h>
16#include <wallet/wallet.h>
17
19
20namespace wallet {
21static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
23{
24 interfaces::Chain& chain = wallet.chain();
25 int confirms = wallet.GetTxDepthInMainChain(wtx);
26 entry.pushKV("confirmations", confirms);
27 if (wtx.IsCoinBase())
28 entry.pushKV("generated", true);
29 if (auto* conf = wtx.state<TxStateConfirmed>())
30 {
31 entry.pushKV("blockhash", conf->confirmed_block_hash.GetHex());
32 entry.pushKV("blockheight", conf->confirmed_block_height);
33 entry.pushKV("blockindex", conf->position_in_block);
34 int64_t block_time;
35 CHECK_NONFATAL(chain.findBlock(conf->confirmed_block_hash, FoundBlock().time(block_time)));
36 entry.pushKV("blocktime", block_time);
37 } else {
38 entry.pushKV("trusted", CachedTxIsTrusted(wallet, wtx));
39 }
40 entry.pushKV("txid", wtx.GetHash().GetHex());
41 entry.pushKV("wtxid", wtx.GetWitnessHash().GetHex());
42 UniValue alternate_wtxids(UniValue::VARR);
43 for (const auto& [wtxid, _] : wtx.GetTxs()) {
44 if (wtxid == wtx.GetWitnessHash()) continue;
45 alternate_wtxids.push_back(wtxid.GetHex());
46 }
47 entry.pushKV("alternate_wtxids", alternate_wtxids);
48 UniValue conflicts(UniValue::VARR);
49 for (const Txid& conflict : wallet.GetTxConflicts(wtx))
50 conflicts.push_back(conflict.GetHex());
51 entry.pushKV("walletconflicts", std::move(conflicts));
52 UniValue mempool_conflicts(UniValue::VARR);
53 for (const Txid& mempool_conflict : wtx.mempool_conflicts)
54 mempool_conflicts.push_back(mempool_conflict.GetHex());
55 entry.pushKV("mempoolconflicts", std::move(mempool_conflicts));
56 entry.pushKV("time", wtx.GetTxTime());
57 entry.pushKV("timereceived", wtx.nTimeReceived);
58
59 // Add opt-in RBF status
60 if (chain.rpcEnableDeprecated("bip125")) {
61 std::string rbfStatus = "no";
62 if (confirms <= 0) {
63 RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.GetTx());
64 if (rbfState == RBFTransactionState::UNKNOWN)
65 rbfStatus = "unknown";
66 else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
67 rbfStatus = "yes";
68 }
69 entry.pushKV("bip125-replaceable", rbfStatus);
70 }
71
72 if (wtx.m_comment) entry.pushKV("comment", *wtx.m_comment);
73 if (wtx.m_comment_to) entry.pushKV("to", *wtx.m_comment_to);
74 if (wtx.m_replaces_txid) entry.pushKV("replaces_txid", wtx.m_replaces_txid->ToString());
75 if (wtx.m_replaced_by_txid) entry.pushKV("replaced_by_txid", wtx.m_replaced_by_txid->ToString());
76}
77
79{
81 int nConf{std::numeric_limits<int>::max()};
82 std::vector<Txid> txids;
83 tallyitem() = default;
84};
85
86static UniValue ListReceived(const CWallet& wallet, const UniValue& params, const bool by_label, const bool include_immature_coinbase) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
87{
88 // Minimum confirmations
89 int nMinDepth = 1;
90 if (!params[0].isNull())
91 nMinDepth = params[0].getInt<int>();
92
93 // Whether to include empty labels
94 bool fIncludeEmpty = false;
95 if (!params[1].isNull())
96 fIncludeEmpty = params[1].get_bool();
97
98 std::optional<CTxDestination> filtered_address{std::nullopt};
99 if (!by_label && !params[3].isNull() && !params[3].get_str().empty()) {
100 if (!IsValidDestinationString(params[3].get_str())) {
101 throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
102 }
103 filtered_address = DecodeDestination(params[3].get_str());
104 }
105
106 // Tally
107 std::map<CTxDestination, tallyitem> mapTally;
108 for (const auto& [_, wtx] : wallet.mapWallet) {
109
110 int nDepth = wallet.GetTxDepthInMainChain(wtx);
111 if (nDepth < nMinDepth)
112 continue;
113
114 // Coinbase with less than 1 confirmation is no longer in the main chain
115 if ((wtx.IsCoinBase() && (nDepth < 1))
116 || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) {
117 continue;
118 }
119
120 for (const CTxOut& txout : wtx.GetTx()->vout) {
121 CTxDestination address;
122 if (!ExtractDestination(txout.scriptPubKey, address))
123 continue;
124
125 if (filtered_address && !(filtered_address == address)) {
126 continue;
127 }
128
129 if (!wallet.IsMine(address))
130 continue;
131
132 tallyitem& item = mapTally[address];
133 item.nAmount += txout.nValue;
134 item.nConf = std::min(item.nConf, nDepth);
135 item.txids.push_back(wtx.GetHash());
136 }
137 }
138
139 // Reply
141 std::map<std::string, tallyitem> label_tally;
142
143 const auto& func = [&](const CTxDestination& address, const std::string& label, bool is_change,
144 const std::optional<AddressPurpose>& purpose) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) {
145 if (is_change) return; // no change addresses
146
147 // Entries in mapTally are only ever added for wallet.IsMine() addresses (see the tally
148 // loop above), so it's only addresses missing from mapTally that need the IsMine() check.
149 auto it = mapTally.find(address);
150 if (it == mapTally.end()) {
151 if (!fIncludeEmpty) return;
152 if (!wallet.IsMine(address)) return; // exclude addresses not owned by the wallet (e.g. "send" purpose)
153 }
154
155 CAmount nAmount = 0;
156 int nConf = std::numeric_limits<int>::max();
157 if (it != mapTally.end()) {
158 nAmount = (*it).second.nAmount;
159 nConf = (*it).second.nConf;
160 }
161
162 if (by_label) {
163 tallyitem& _item = label_tally[label];
164 _item.nAmount += nAmount;
165 _item.nConf = std::min(_item.nConf, nConf);
166 } else {
168 obj.pushKV("address", EncodeDestination(address));
169 obj.pushKV("amount", ValueFromAmount(nAmount));
170 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
171 obj.pushKV("label", label);
172 UniValue transactions(UniValue::VARR);
173 if (it != mapTally.end()) {
174 for (const Txid& _item : (*it).second.txids) {
175 transactions.push_back(_item.GetHex());
176 }
177 }
178 obj.pushKV("txids", std::move(transactions));
179 ret.push_back(std::move(obj));
180 }
181 };
182
183 if (filtered_address) {
184 const auto& entry = wallet.FindAddressBookEntry(*filtered_address, /*allow_change=*/false);
185 if (entry) func(*filtered_address, entry->GetLabel(), entry->IsChange(), entry->purpose);
186 } else {
187 // No filtered addr, walk-through the addressbook entry
188 wallet.ForEachAddrBookEntry(func);
189 }
190
191 if (by_label) {
192 for (const auto& entry : label_tally) {
193 CAmount nAmount = entry.second.nAmount;
194 int nConf = entry.second.nConf;
196 obj.pushKV("amount", ValueFromAmount(nAmount));
197 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
198 obj.pushKV("label", entry.first);
199 ret.push_back(std::move(obj));
200 }
201 }
202
203 return ret;
204}
205
207{
208 return RPCMethod{
209 "listreceivedbyaddress",
210 "List balances by receiving address.\n",
211 {
212 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
213 {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},
214 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
215 {"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If present and non-empty, only return information on this address."},
216 {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
217 },
218 RPCResult{
219 RPCResult::Type::ARR, "", "",
220 {
221 {RPCResult::Type::OBJ, "", "",
222 {
223 {RPCResult::Type::STR, "address", "The receiving address"},
224 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
225 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
226 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
227 {RPCResult::Type::ARR, "txids", "",
228 {
229 {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},
230 }},
231 }},
232 }
233 },
235 HelpExampleCli("listreceivedbyaddress", "")
236 + HelpExampleCli("listreceivedbyaddress", "6 true")
237 + HelpExampleCli("listreceivedbyaddress", "6 true true \"\" true")
238 + HelpExampleRpc("listreceivedbyaddress", "6, true, true")
239 + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\", true")
240 },
241 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
242{
243 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
244 if (!pwallet) return UniValue::VNULL;
245
246 // Make sure the results are valid at least up to the most recent block
247 // the user could have gotten from another RPC command prior to now
248 pwallet->BlockUntilSyncedToCurrentChain();
249
250 const bool include_immature_coinbase{request.params[4].isNull() ? false : request.params[4].get_bool()};
251
252 LOCK(pwallet->cs_wallet);
253
254 return ListReceived(*pwallet, request.params, false, include_immature_coinbase);
255},
256 };
257}
258
260{
261 return RPCMethod{
262 "listreceivedbylabel",
263 "List received transactions by label.\n",
264 {
265 {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
266 {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include labels that haven't received any payments."},
267 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
268 {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
269 },
270 RPCResult{
271 RPCResult::Type::ARR, "", "",
272 {
273 {RPCResult::Type::OBJ, "", "",
274 {
275 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
276 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
277 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
278 }},
279 }
280 },
282 HelpExampleCli("listreceivedbylabel", "")
283 + HelpExampleCli("listreceivedbylabel", "6 true")
284 + HelpExampleRpc("listreceivedbylabel", "6, true, true, true")
285 },
286 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
287{
288 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
289 if (!pwallet) return UniValue::VNULL;
290
291 // Make sure the results are valid at least up to the most recent block
292 // the user could have gotten from another RPC command prior to now
293 pwallet->BlockUntilSyncedToCurrentChain();
294
295 const bool include_immature_coinbase{request.params[3].isNull() ? false : request.params[3].get_bool()};
296
297 LOCK(pwallet->cs_wallet);
298
299 return ListReceived(*pwallet, request.params, true, include_immature_coinbase);
300},
301 };
302}
303
304static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
305{
306 if (IsValidDestination(dest)) {
307 entry.pushKV("address", EncodeDestination(dest));
308 }
309}
310
321template <class Vec>
322static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong,
323 Vec& ret, const std::optional<std::string>& filter_label,
324 bool include_change = false)
326{
327 CAmount nFee;
328 std::list<COutputEntry> listReceived;
329 std::list<COutputEntry> listSent;
330
331 CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, include_change);
332
333 // Sent
334 if (!filter_label.has_value())
335 {
336 for (const COutputEntry& s : listSent)
337 {
339 MaybePushAddress(entry, s.destination);
340 entry.pushKV("category", "send");
341 entry.pushKV("amount", ValueFromAmount(-s.amount));
342 const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
343 if (address_book_entry) {
344 entry.pushKV("label", address_book_entry->GetLabel());
345 }
346 entry.pushKV("vout", s.vout);
347 entry.pushKV("fee", ValueFromAmount(-nFee));
348 if (fLong)
349 WalletTxToJSON(wallet, wtx, entry);
350 entry.pushKV("abandoned", wtx.isAbandoned());
351 ret.push_back(std::move(entry));
352 }
353 }
354
355 // Received
356 if (listReceived.size() > 0 && wallet.GetTxDepthInMainChain(wtx) >= nMinDepth) {
357 for (const COutputEntry& r : listReceived)
358 {
359 std::string label;
360 const auto* address_book_entry = wallet.FindAddressBookEntry(r.destination);
361 if (address_book_entry) {
362 label = address_book_entry->GetLabel();
363 }
364 if (filter_label.has_value() && label != filter_label.value()) {
365 continue;
366 }
368 MaybePushAddress(entry, r.destination);
369 PushParentDescriptors(wallet, wtx.GetTx()->vout.at(r.vout).scriptPubKey, entry);
370 if (wtx.IsCoinBase())
371 {
372 if (wallet.GetTxDepthInMainChain(wtx) < 1)
373 entry.pushKV("category", "orphan");
374 else if (wallet.IsTxImmatureCoinBase(wtx))
375 entry.pushKV("category", "immature");
376 else
377 entry.pushKV("category", "generate");
378 }
379 else
380 {
381 entry.pushKV("category", "receive");
382 }
383 entry.pushKV("amount", ValueFromAmount(r.amount));
384 if (address_book_entry) {
385 entry.pushKV("label", label);
386 }
387 entry.pushKV("vout", r.vout);
388 entry.pushKV("abandoned", wtx.isAbandoned());
389 if (fLong)
390 WalletTxToJSON(wallet, wtx, entry);
391 ret.push_back(std::move(entry));
392 }
393 }
394}
395
396static void PushTxAmountAndFee(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
398{
399 CAmount credit = CachedTxGetCredit(wallet, wtx, /*avoid_reuse=*/false);
400 CAmount debit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false);
401 CAmount net = credit - debit;
402 bool is_from_me = CachedTxIsFromMe(wallet, wtx);
403 CAmount fee = (is_from_me ? wtx.GetTx()->GetValueOut() - debit : 0);
404
405 entry.pushKV("amount", ValueFromAmount(net - fee));
406 if (is_from_me)
407 entry.pushKV("fee", ValueFromAmount(fee));
408}
409
410static void PushTxDecoded(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
412{
413 UniValue decoded(UniValue::VOBJ);
414 TxToUniv(*wtx.GetTx(),
415 /*block_hash=*/uint256(),
416 /*entry=*/decoded,
417 /*include_hex=*/false,
418 /*txundo=*/nullptr,
419 /*verbosity=*/TxVerbosity::SHOW_DETAILS,
420 /*is_change_func=*/[&wallet](const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) {
421 AssertLockHeld(wallet.cs_wallet);
422 return OutputIsChange(wallet, txout);
423 });
424 entry.pushKV("decoded", std::move(decoded));
425}
426
435static void ListRawTransaction(const CWallet& wallet, const CWalletTx& wtx, std::vector<UniValue>& ret, bool verbose)
437{
439
440 PushTxAmountAndFee(wallet, wtx, entry);
441
442 WalletTxToJSON(wallet, wtx, entry);
443 entry.pushKV("abandoned", wtx.isAbandoned());
444 entry.pushKV("hex", EncodeHexTx(*wtx.GetTx()));
445
446 if (verbose) {
447 PushTxDecoded(wallet, wtx, entry);
448 }
449
450 ret.push_back(std::move(entry));
451}
452
453static std::vector<RPCResult> TransactionDescriptionString(bool include_parent_descs = true)
454{
455 std::vector<RPCResult> result{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
456 "transaction conflicted that many blocks ago."},
457 {RPCResult::Type::BOOL, "generated", /*optional=*/true, "Only present if the transaction's only input is a coinbase one."},
458 {RPCResult::Type::BOOL, "trusted", /*optional=*/true, "Whether we consider the transaction to be trusted and safe to spend from.\n"
459 "Only present when the transaction has 0 confirmations (or negative confirmations, if conflicted)."},
460 {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash containing the transaction."},
461 {RPCResult::Type::NUM, "blockheight", /*optional=*/true, "The block height containing the transaction."},
462 {RPCResult::Type::NUM, "blockindex", /*optional=*/true, "The index of the transaction in the block that includes it."},
463 {RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
464 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
465 {RPCResult::Type::STR_HEX, "wtxid", "The hash of serialized transaction, including witness data."},
466 {RPCResult::Type::ARR, "alternate_wtxids", "The wtxids of transactions with different witness data but the same txid.",
467 {
468 {RPCResult::Type::STR_HEX, "wtxid", "The witness transaction id."},
469 }},
470 {RPCResult::Type::ARR, "walletconflicts", "Confirmed transactions that have been detected by the wallet to conflict with this transaction.",
471 {
472 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
473 }},
474 {RPCResult::Type::STR_HEX, "replaced_by_txid", /*optional=*/true, "The txid of the transaction that replaced this one."},
475 {RPCResult::Type::STR_HEX, "replaces_txid", /*optional=*/true, "The txid of the transaction that this one replaces."},
476 {RPCResult::Type::ARR, "mempoolconflicts", "Transactions in the mempool that directly conflict with either this transaction or an ancestor transaction",
477 {
478 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
479 }},
480 {RPCResult::Type::STR, "to", /*optional=*/true, "If a comment to is associated with the transaction."},
481 {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
482 {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
483 {RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction, only present if not empty."},
484 {RPCResult::Type::STR, "bip125-replaceable", /*optional=*/true, "(\"yes|no|unknown\") (DEPRECATED) Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"
485 "May be unknown for unconfirmed transactions not in the mempool because their unconfirmed ancestors are unknown."},
486 };
487 if (include_parent_descs) {
488 result.push_back({RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'receive'. List of parent descriptors for the output script of this coin.", {
489 {RPCResult::Type::STR, "desc", "The descriptor string."},
490 }});
491 }
492 return result;
493}
494
496{
497 return RPCMethod{
498 "listtransactions",
499 "If a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
500 "Returns up to 'count' most recent transactions ordered from oldest to newest while skipping the first number of \n"
501 "transactions specified in the 'skip' argument. A transaction can have multiple entries in this RPC response. \n"
502 "For instance, a wallet transaction that pays three addresses — one wallet-owned and two external — will produce \n"
503 "four entries. The payment to the wallet-owned address appears both as a send entry and as a receive entry. \n"
504 "As a result, the RPC response will contain one entry in the receive category and three entries in the send category.\n",
505 {
506 {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If set, should be a valid label name to return only incoming transactions\n"
507 "with the specified label, or \"*\" to disable filtering and return all transactions."},
508 {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
509 {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
510 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
511 },
512 RPCResult{
513 RPCResult::Type::ARR, "", "",
514 {
515 {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
516 {
517 {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
518 {RPCResult::Type::STR, "category", "The transaction category.\n"
519 "\"send\" Transactions sent.\n"
520 "\"receive\" Non-coinbase transactions received.\n"
521 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
522 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
523 "\"orphan\" Orphaned coinbase transactions received."},
524 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
525 "for all other categories"},
526 {RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
527 {RPCResult::Type::NUM, "vout", "the vout value"},
528 {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
529 "'send' category of transactions."},
530 },
532 {
533 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
534 })},
535 }
536 },
538 "\nList the most recent 10 transactions in the systems\n"
539 + HelpExampleCli("listtransactions", "") +
540 "\nList transactions 100 to 120\n"
541 + HelpExampleCli("listtransactions", "\"*\" 20 100") +
542 "\nAs a JSON-RPC call\n"
543 + HelpExampleRpc("listtransactions", "\"*\", 20, 100")
544 },
545 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
546{
547 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
548 if (!pwallet) return UniValue::VNULL;
549
550 // Make sure the results are valid at least up to the most recent block
551 // the user could have gotten from another RPC command prior to now
552 pwallet->BlockUntilSyncedToCurrentChain();
553
554 std::optional<std::string> filter_label;
555 if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
556 filter_label.emplace(LabelFromValue(request.params[0]));
557 if (filter_label.value().empty()) {
558 throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
559 }
560 }
561 int nCount = 10;
562 if (!request.params[1].isNull())
563 nCount = request.params[1].getInt<int>();
564 int nFrom = 0;
565 if (!request.params[2].isNull())
566 nFrom = request.params[2].getInt<int>();
567
568 if (nCount < 0)
569 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
570 if (nFrom < 0)
571 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
572
573 std::vector<UniValue> ret;
574 {
575 LOCK(pwallet->cs_wallet);
576
577 const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
578
579 // iterate backwards until we have nCount items to return:
580 for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
581 {
582 CWalletTx *const pwtx = (*it).second;
583 ListTransactions(*pwallet, *pwtx, 0, true, ret, filter_label);
584 if ((int)ret.size() >= (nCount+nFrom)) break;
585 }
586 }
587
588 // ret is newest to oldest
589
590 if (nFrom > (int)ret.size())
591 nFrom = ret.size();
592 if ((nFrom + nCount) > (int)ret.size())
593 nCount = ret.size() - nFrom;
594
595 auto txs_rev_it{std::make_move_iterator(ret.rend())};
596 UniValue result{UniValue::VARR};
597 result.push_backV(txs_rev_it - nFrom - nCount, txs_rev_it - nFrom); // Return oldest to newest
598 return result;
599},
600 };
601}
602
604{
605 return RPCMethod{
606 "listrawtransactions",
607 "Returns up to 'count' most recent wallet transactions ordered from oldest to newest, "
608 "skipping the first 'skip' transactions. Unlike `listtransactions`, each wallet transaction "
609 "appears exactly once with its net wallet balance change excluding fee, without logical "
610 "interpretation (no category assignment, no change suppression). This means consolidation "
611 "and self-transfer transactions that are invisible in `listtransactions` are included here.\n",
612 {
613 {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return."},
614 {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip."},
615 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
616 },
617 RPCResult{
618 RPCResult::Type::ARR, "", "",
619 {
620 {RPCResult::Type::OBJ, "", "", Cat<std::vector<RPCResult>>(
621 {
622 {RPCResult::Type::STR_AMOUNT, "amount", "The net change to the wallet balance caused by this transaction "
623 "(excluding fee). Positive means the wallet gained funds, negative means it lost funds, "
624 "zero means a pure self-transfer (e.g. consolidation)."},
625 {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The fee paid in " + CURRENCY_UNIT + ". "
626 "This is negative and only present when the wallet funded the transaction."},
627 },
628 Cat(TransactionDescriptionString(/*include_parent_descs=*/false),
629 {
630 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
631 {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
632 {RPCResult::Type::OBJ, "decoded", /*optional=*/true, "The decoded transaction (only present when `verbose` is passed)",
633 {
634 TxDoc({.wallet = true}),
635 }},
636 }))},
637 }
638 },
640 "\nList the most recent 10 transactions\n"
641 + HelpExampleCli("listrawtransactions", "") +
642 "\nList transactions 100 to 120\n"
643 + HelpExampleCli("listrawtransactions", "20 100") +
644 "\nList the most recent 10 transactions with decoded transaction data\n"
645 + HelpExampleCli("listrawtransactions", "10 0 true") +
646 "\nAs a JSON-RPC call\n"
647 + HelpExampleRpc("listrawtransactions", "20, 100")
648 },
649 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
650{
651 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
652 if (!pwallet) return UniValue::VNULL;
653
654 pwallet->BlockUntilSyncedToCurrentChain();
655
656 int count = self.Arg<int>("count");
657 int skip = self.Arg<int>("skip");
658 bool verbose = self.Arg<bool>("verbose");
659
660 if (count < 0)
661 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
662 if (skip < 0)
663 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip");
664
665 std::vector<UniValue> ret;
666 {
667 LOCK(pwallet->cs_wallet);
668
669 const CWallet::TxItems& tx_ordered = pwallet->wtxOrdered;
670
671 int skipped = 0;
672 for (CWallet::TxItems::const_reverse_iterator it = tx_ordered.rbegin(); it != tx_ordered.rend(); ++it) {
673 if ((int)ret.size() >= count) break;
674 if (skipped++ < skip) continue;
675 CWalletTx* const pwtx = (*it).second;
676 ListRawTransaction(*pwallet, *pwtx, ret, verbose);
677 }
678 }
679
680 UniValue result{UniValue::VARR};
681 result.push_backV(std::make_move_iterator(ret.rbegin()), std::make_move_iterator(ret.rend()));
682 return result;
683},
684 };
685}
686
687static std::vector<RPCResult> ListSinceBlockTxFields()
688{
689 return Cat<std::vector<RPCResult>>(
690 {
691 {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
692 {RPCResult::Type::STR, "category", "The transaction category.\n"
693 "\"send\" Transactions sent.\n"
694 "\"receive\" Non-coinbase transactions received.\n"
695 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
696 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
697 "\"orphan\" Orphaned coinbase transactions received."},
698 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
699 "for all other categories"},
700 {RPCResult::Type::NUM, "vout", "the vout value"},
701 {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
702 "'send' category of transactions."},
703 },
704 Cat(
706 std::vector<RPCResult>{
707 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
708 {RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
709 }
710 )
711 );
712}
713
715{
716 return RPCMethod{
717 "listsinceblock",
718 "Get all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
719 "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
720 "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
721 {
722 {"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If set, the block hash to list transactions since, otherwise list all transactions."},
723 {"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
724 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
725 {"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
726 "(not guaranteed to work on pruned nodes)"},
727 {"include_change", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also add entries for change outputs.\n"},
728 {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Return only incoming transactions paying to addresses with the specified label.\n"},
729 },
730 RPCResult{
731 RPCResult::Type::OBJ, "", "",
732 {
733 {RPCResult::Type::ARR, "transactions", "",
734 {
736 }},
737 {RPCResult::Type::ARR, "removed", /*optional=*/true, "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
738 "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.",
739 {
740 {RPCResult::Type::OBJ, "", "", ListSinceBlockTxFields(), {.print_elision = std::string{}}},
741 }},
742 {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
743 }
744 },
746 HelpExampleCli("listsinceblock", "")
747 + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
748 + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
749 },
750 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
751{
752 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
753 if (!pwallet) return UniValue::VNULL;
754
755 const CWallet& wallet = *pwallet;
756 // Make sure the results are valid at least up to the most recent block
757 // the user could have gotten from another RPC command prior to now
758 wallet.BlockUntilSyncedToCurrentChain();
759
760 LOCK(wallet.cs_wallet);
761
762 std::optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
763 std::optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
764 int target_confirms = 1;
765
766 uint256 blockId;
767 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
768 blockId = ParseHashV(request.params[0], "blockhash");
769 height = int{};
770 altheight = int{};
771 if (!wallet.chain().findCommonAncestor(blockId, wallet.GetLastBlockHash(), /*ancestor_out=*/FoundBlock().height(*height), /*block1_out=*/FoundBlock().height(*altheight))) {
772 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
773 }
774 }
775
776 if (!request.params[1].isNull()) {
777 target_confirms = request.params[1].getInt<int>();
778
779 if (target_confirms < 1) {
780 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
781 }
782 }
783
784 bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
785 bool include_change = (!request.params[4].isNull() && request.params[4].get_bool());
786
787 // Only set it if 'label' was provided.
788 std::optional<std::string> filter_label;
789 if (!request.params[5].isNull()) filter_label.emplace(LabelFromValue(request.params[5]));
790
791 int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
792
793 UniValue transactions(UniValue::VARR);
794
795 for (const auto& [_, tx] : wallet.mapWallet) {
796
797 if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
798 ListTransactions(wallet, tx, 0, true, transactions, filter_label, include_change);
799 }
800 }
801
802 // when a reorg'd block is requested, we also list any relevant transactions
803 // in the blocks of the chain that was detached
804 UniValue removed(UniValue::VARR);
805 while (include_removed && altheight && *altheight > *height) {
806 CBlock block;
807 if (!wallet.chain().findBlock(blockId, FoundBlock().data(block)) || block.IsNull()) {
808 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
809 }
810 for (const CTransactionRef& tx : block.vtx) {
811 auto it = wallet.mapWallet.find(tx->GetHash());
812 if (it != wallet.mapWallet.end()) {
813 // We want all transactions regardless of confirmation count to appear here,
814 // even negative confirmation ones, hence the big negative.
815 ListTransactions(wallet, it->second, -100000000, true, removed, filter_label, include_change);
816 }
817 }
818 blockId = block.hashPrevBlock;
819 --*altheight;
820 }
821
822 uint256 lastblock;
823 target_confirms = std::min(target_confirms, wallet.GetLastBlockHeight() + 1);
824 CHECK_NONFATAL(wallet.chain().findAncestorByHeight(wallet.GetLastBlockHash(), wallet.GetLastBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
825
827 ret.pushKV("transactions", std::move(transactions));
828 if (include_removed) ret.pushKV("removed", std::move(removed));
829 ret.pushKV("lastblock", lastblock.GetHex());
830
831 return ret;
832},
833 };
834}
835
837{
838 return RPCMethod{
839 "gettransaction",
840 "Get detailed information about in-wallet transaction <txid>\n",
841 {
842 {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
843 {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
844 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
845 "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
846 },
847 RPCResult{
848 RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
849 {
850 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
851 {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
852 "'send' category of transactions."},
853 },
855 {
856 {RPCResult::Type::ARR, "details", "",
857 {
858 {RPCResult::Type::OBJ, "", "",
859 {
860 {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address involved in the transaction."},
861 {RPCResult::Type::STR, "category", "The transaction category.\n"
862 "\"send\" Transactions sent.\n"
863 "\"receive\" Non-coinbase transactions received.\n"
864 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
865 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
866 "\"orphan\" Orphaned coinbase transactions received."},
867 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
868 {RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
869 {RPCResult::Type::NUM, "vout", "the vout value"},
870 {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
871 "'send' category of transactions."},
872 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
873 {RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'receive'. List of parent descriptors for the output script of this coin.", {
874 {RPCResult::Type::STR, "desc", "The descriptor string."},
875 }},
876 }},
877 }},
878 {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
879 {RPCResult::Type::OBJ, "decoded", /*optional=*/true, "The decoded transaction (only present when `verbose` is passed)",
880 {
881 TxDoc({.wallet = true}),
882 }},
884 })
885 },
887 HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
888 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
889 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
890 + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
891 },
892 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
893{
894 const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
895 if (!pwallet) return UniValue::VNULL;
896
897 // Make sure the results are valid at least up to the most recent block
898 // the user could have gotten from another RPC command prior to now
899 pwallet->BlockUntilSyncedToCurrentChain();
900
901 LOCK(pwallet->cs_wallet);
902
903 Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
904
905 bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
906
908 auto it = pwallet->mapWallet.find(hash);
909 if (it == pwallet->mapWallet.end()) {
910 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
911 }
912 const CWalletTx& wtx = it->second;
913
914 PushTxAmountAndFee(*pwallet, wtx, entry);
915
916 WalletTxToJSON(*pwallet, wtx, entry);
917
918 UniValue details(UniValue::VARR);
919 ListTransactions(*pwallet, wtx, 0, false, details, /*filter_label=*/std::nullopt);
920 entry.pushKV("details", std::move(details));
921
922 entry.pushKV("hex", EncodeHexTx(*wtx.GetTx()));
923
924 if (verbose) {
925 PushTxDecoded(*pwallet, wtx, entry);
926 }
927
928 AppendLastProcessedBlock(entry, *pwallet);
929 return entry;
930},
931 };
932}
933
935{
936 return RPCMethod{
937 "abandontransaction",
938 "Mark in-wallet transaction <txid> as abandoned\n"
939 "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
940 "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
941 "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
942 "It has no effect on transactions which are already abandoned.\n",
943 {
944 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
945 },
948 HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
949 + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
950 },
951 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
952{
953 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
954 if (!pwallet) return UniValue::VNULL;
955
956 // Make sure the results are valid at least up to the most recent block
957 // the user could have gotten from another RPC command prior to now
958 pwallet->BlockUntilSyncedToCurrentChain();
959
960 LOCK(pwallet->cs_wallet);
961
962 Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
963
964 if (!pwallet->mapWallet.contains(hash)) {
965 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
966 }
967 if (!pwallet->AbandonTransaction(hash)) {
968 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
969 }
970
971 return UniValue::VNULL;
972},
973 };
974}
975
977{
978 return RPCMethod{
979 "rescanblockchain",
980 "Rescan the local blockchain for wallet related transactions.\n"
981 "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
982 "The rescan is significantly faster if block filters are available\n"
983 "(using startup option \"-blockfilterindex=1\").\n",
984 {
985 {"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "block height where the rescan should start"},
986 {"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
987 },
988 RPCResult{
989 RPCResult::Type::OBJ, "", "",
990 {
991 {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"},
992 {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."},
993 }
994 },
996 HelpExampleCli("rescanblockchain", "100000 120000")
997 + HelpExampleRpc("rescanblockchain", "100000, 120000")
998 },
999 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1000{
1001 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1002 if (!pwallet) return UniValue::VNULL;
1003 CWallet& wallet{*pwallet};
1004
1005 // Make sure the results are valid at least up to the most recent block
1006 // the user could have gotten from another RPC command prior to now
1007 wallet.BlockUntilSyncedToCurrentChain();
1008
1009 WalletRescanReserver reserver(*pwallet);
1010 if (!reserver.reserve(/*with_passphrase=*/true)) {
1011 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
1012 }
1013
1014 int start_height = 0;
1015 std::optional<int> stop_height;
1016 uint256 start_block;
1017
1018 LOCK(pwallet->m_relock_mutex);
1019 {
1020 LOCK(pwallet->cs_wallet);
1021 EnsureWalletIsUnlocked(*pwallet);
1022 int tip_height = pwallet->GetLastBlockHeight();
1023
1024 if (!request.params[0].isNull()) {
1025 start_height = request.params[0].getInt<int>();
1026 if (start_height < 0 || start_height > tip_height) {
1027 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
1028 }
1029 }
1030
1031 if (!request.params[1].isNull()) {
1032 stop_height = request.params[1].getInt<int>();
1033 if (*stop_height < 0 || *stop_height > tip_height) {
1034 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
1035 } else if (*stop_height < start_height) {
1036 throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
1037 }
1038 }
1039
1040 // We can't rescan unavailable blocks, stop and throw an error
1041 if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(), start_height, stop_height)) {
1042 if (pwallet->chain().havePruned() && pwallet->chain().getPruneHeight() >= start_height) {
1043 throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
1044 }
1045 if (pwallet->chain().hasAssumedValidChain()) {
1046 throw JSONRPCError(RPC_MISC_ERROR, "Failed to rescan unavailable blocks likely due to an in-progress assumeutxo background sync. Check logs or getchainstates RPC for assumeutxo background sync progress and try again later.");
1047 }
1048 throw JSONRPCError(RPC_MISC_ERROR, "Failed to rescan unavailable blocks, potentially caused by data corruption. If the issue persists you may want to reindex (see -reindex option).");
1049 }
1050
1051 CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), start_height, FoundBlock().hash(start_block)));
1052 }
1053
1054 ScanResult result =
1055 pwallet->Scanner().Scan(start_block, start_height, stop_height, reserver, /*save_progress=*/false);
1056 switch (result.status) {
1058 break;
1060 throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
1062 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
1063 } // no default case, so the compiler can warn about missing cases
1064 UniValue response(UniValue::VOBJ);
1065 response.pushKV("start_height", start_height);
1066 response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
1067 return response;
1068},
1069 };
1070}
1071
1073{
1074 return RPCMethod{"abortrescan",
1075 "Stops current wallet rescan triggered by an RPC call, e.g. by a rescanblockchain call.\n"
1076 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
1077 {},
1078 RPCResult{RPCResult::Type::BOOL, "", "Whether the abort was successful"},
1080 "\nImport a private key\n"
1081 + HelpExampleCli("rescanblockchain", "") +
1082 "\nAbort the running wallet rescan\n"
1083 + HelpExampleCli("abortrescan", "") +
1084 "\nAs a JSON-RPC call\n"
1085 + HelpExampleRpc("abortrescan", "")
1086 },
1087 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1088{
1089 std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
1090 if (!pwallet) return UniValue::VNULL;
1091
1092 if (!pwallet->Scanner().IsScanning() || pwallet->Scanner().IsAborting()) return false;
1093 pwallet->Scanner().Abort();
1094 return true;
1095},
1096 };
1097}
1098} // namespace wallet
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a scriptPubKey for the destination.
Definition: addresstype.cpp:49
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:143
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
int ret
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:112
uint256 hashPrevBlock
Definition: block.h:31
bool IsNull() const
Definition: block.h:54
Definition: block.h:74
std::vector< CTransactionRef > vtx
Definition: block.h:77
An output of a transaction.
Definition: transaction.h:141
auto Arg(std::string_view key) const
Helper to get a required or default-valued request argument.
Definition: util.h:470
void push_back(UniValue val)
Definition: univalue.cpp:103
@ VNULL
Definition: univalue.h:24
@ VOBJ
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
std::string GetHex() const
Definition: uint256.cpp:11
Interface giving clients (wallet processes, maybe other analysis tools in the future) ability to acce...
Definition: chain.h:117
virtual bool rpcEnableDeprecated(const std::string &method)=0
Check if deprecated RPC is enabled.
virtual RBFTransactionState isRBFOptIn(const CTransaction &tx)=0
Check if transaction is RBF opt in.
virtual bool findBlock(const uint256 &hash, const FoundBlock &block={})=0
Return whether node has the block and optionally return block metadata or contents.
Helper for findBlock to selectively return pieces of block data.
Definition: chain.h:52
std::string GetHex() const
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:196
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:318
std::multimap< int64_t, CWalletTx * > TxItems
Definition: wallet.h:484
A transaction with a bunch of additional info that only the owner cares about.
Definition: transaction.h:194
bool IsCoinBase() const
Definition: transaction.h:392
bool isAbandoned() const
Definition: transaction.h:384
CTransactionRef GetTx() const
Definition: transaction.h:351
RAII object to check and reserve a wallet rescan.
Definition: scan.h:37
bool reserve(bool with_passphrase=false)
Definition: scan.cpp:40
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
std::string EncodeHexTx(const CTransaction &tx)
Definition: core_io.cpp:404
void TxToUniv(const CTransaction &tx, const uint256 &block_hash, UniValue &entry, bool include_hex, const CTxUndo *txundo, TxVerbosity verbosity, std::function< bool(const CTxOut &)> is_change_func)
Definition: core_io.cpp:432
UniValue ValueFromAmount(const CAmount amount)
Definition: core_io.cpp:283
@ SHOW_DETAILS
Include TXID, inputs, outputs, and other common block's transaction information.
const std::string CURRENCY_UNIT
Definition: feerate.h:19
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: key_io.cpp:308
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:297
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:292
uint64_t fee
RPCMethod abandontransaction()
RPCMethod abortrescan()
RPCMethod gettransaction()
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:62
bool CachedTxIsFromMe(const CWallet &wallet, const CWalletTx &wtx)
Definition: receive.cpp:196
RPCMethod listsinceblock()
bool CachedTxIsTrusted(const CWallet &wallet, const CWalletTx &wtx, std::set< Txid > &trusted_parents)
Definition: receive.cpp:205
const RPCResult RESULT_LAST_PROCESSED_BLOCK
Definition: util.h:29
RPCMethod listrawtransactions()
void EnsureWalletIsUnlocked(const CWallet &wallet)
Definition: util.cpp:85
RPCMethod rescanblockchain()
RPCMethod listtransactions()
void PushParentDescriptors(const CWallet &wallet, const CScript &script_pubkey, UniValue &entry)
Fetch parent descriptors of this scriptPubKey.
Definition: util.cpp:112
static void PushTxAmountAndFee(const CWallet &wallet, const CWalletTx &wtx, UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
static void ListRawTransaction(const CWallet &wallet, const CWalletTx &wtx, std::vector< UniValue > &ret, bool verbose) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Append a raw transaction entry for the given wallet transaction to ret.
static std::vector< RPCResult > ListSinceBlockTxFields()
RPCMethod listreceivedbyaddress()
static void PushTxDecoded(const CWallet &wallet, const CWalletTx &wtx, UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
static std::vector< RPCResult > TransactionDescriptionString(bool include_parent_descs=true)
std::string LabelFromValue(const UniValue &value)
Definition: util.cpp:101
CAmount CachedTxGetDebit(const CWallet &wallet, const CWalletTx &wtx, bool avoid_reuse)
Definition: receive.cpp:122
void AppendLastProcessedBlock(UniValue &entry, const CWallet &wallet)
Definition: util.cpp:168
CAmount CachedTxGetCredit(const CWallet &wallet, const CWalletTx &wtx, bool avoid_reuse)
Definition: receive.cpp:110
static UniValue ListReceived(const CWallet &wallet, const UniValue &params, const bool by_label, const bool include_immature_coinbase) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
void CachedTxGetAmounts(const CWallet &wallet, const CWalletTx &wtx, std::list< COutputEntry > &listReceived, std::list< COutputEntry > &listSent, CAmount &nFee, bool include_change)
Definition: receive.cpp:139
static void ListTransactions(const CWallet &wallet, const CWalletTx &wtx, int nMinDepth, bool fLong, Vec &ret, const std::optional< std::string > &filter_label, bool include_change=false) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
List transactions based on the given criteria.
RPCMethod listreceivedbylabel()
static void WalletTxToJSON(const CWallet &wallet, const CWalletTx &wtx, UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
static void MaybePushAddress(UniValue &entry, const CTxDestination &dest)
RBFTransactionState
The rbf state of unconfirmed transactions.
Definition: rbf.h:29
@ UNKNOWN
Unconfirmed tx that does not signal rbf and is not in the mempool.
@ REPLACEABLE_BIP125
Either this tx or a mempool ancestor signals rbf.
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:417
std::vector< RPCResult > TxDoc(const TxDocOptions &opts)
Explain the UniValue "decoded" transaction object, may include extra fields if processed by wallet.
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:75
@ RPC_MISC_ERROR
General application defined errors.
Definition: protocol.h:65
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:69
@ RPC_WALLET_ERROR
Wallet errors.
Definition: protocol.h:97
@ RPC_INTERNAL_ERROR
Definition: protocol.h:61
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:67
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:189
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:207
const std::string UNIX_EPOCH_TIME
String used to describe UNIX epoch time in documentation, factored out to a constant for consistency.
Definition: util.cpp:49
const std::string EXAMPLE_ADDRESS[2]
Example bech32 addresses for the RPCExamples help documentation.
Definition: util.cpp:50
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: util.cpp:123
@ STR_HEX
Special type that is a STR with only hex chars.
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
@ NO
Required arg.
@ NUM_TIME
Special numeric to denote unix epoch time.
@ STR_HEX
Special string with only hex chars.
@ STR_AMOUNT
Special string to represent a floating point amount.
Definition: receive.h:32
Result of a wallet scan.
Definition: scan.h:19
enum wallet::ScanResult::@19 status
std::optional< int > last_scanned_height
Definition: scan.h:26
State of transaction confirmed in a block.
Definition: transaction.h:34
std::vector< Txid > txids
tallyitem()=default
#define LOCK(cs)
Definition: sync.h:268
static int count
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
V Cat(V v1, V &&v2)
Concatenate two vectors, moving elements.
Definition: vector.h:34