Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <core_io.h>6#include <key_io.h>7#include <policy/rbf.h>8#include <primitives/transaction_identifier.h>9#include <rpc/util.h>10#include <rpc/rawtransaction_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/wallet.h>17using interfaces::FoundBlock;19namespace wallet {20static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)21EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)22{23interfaces::Chain& chain = wallet.chain();24int confirms = wallet.GetTxDepthInMainChain(wtx);25entry.pushKV("confirmations", confirms);26if (wtx.IsCoinBase())27entry.pushKV("generated", true);28if (auto* conf = wtx.state<TxStateConfirmed>())29{30entry.pushKV("blockhash", conf->confirmed_block_hash.GetHex());31entry.pushKV("blockheight", conf->confirmed_block_height);32entry.pushKV("blockindex", conf->position_in_block);33int64_t block_time;34CHECK_NONFATAL(chain.findBlock(conf->confirmed_block_hash, FoundBlock().time(block_time)));35entry.pushKV("blocktime", block_time);36} else {37entry.pushKV("trusted", CachedTxIsTrusted(wallet, wtx));38}39entry.pushKV("txid", wtx.GetHash().GetHex());40entry.pushKV("wtxid", wtx.GetWitnessHash().GetHex());41UniValue alternate_wtxids(UniValue::VARR);42for (const auto& [wtxid, _] : wtx.GetTxs()) {43if (wtxid == wtx.GetWitnessHash()) continue;44alternate_wtxids.push_back(wtxid.GetHex());45}46entry.pushKV("alternate_wtxids", alternate_wtxids);47UniValue conflicts(UniValue::VARR);48for (const Txid& conflict : wallet.GetTxConflicts(wtx))49conflicts.push_back(conflict.GetHex());50entry.pushKV("walletconflicts", std::move(conflicts));51UniValue mempool_conflicts(UniValue::VARR);52for (const Txid& mempool_conflict : wtx.mempool_conflicts)53mempool_conflicts.push_back(mempool_conflict.GetHex());54entry.pushKV("mempoolconflicts", std::move(mempool_conflicts));55entry.pushKV("time", wtx.GetTxTime());56entry.pushKV("timereceived", wtx.nTimeReceived);58// Add opt-in RBF status59if (chain.rpcEnableDeprecated("bip125")) {60std::string rbfStatus = "no";61if (confirms <= 0) {62RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.GetTx());63if (rbfState == RBFTransactionState::UNKNOWN)64rbfStatus = "unknown";65else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)66rbfStatus = "yes";67}68entry.pushKV("bip125-replaceable", rbfStatus);69}71if (wtx.m_comment) entry.pushKV("comment", *wtx.m_comment);72if (wtx.m_comment_to) entry.pushKV("to", *wtx.m_comment_to);73if (wtx.m_replaces_txid) entry.pushKV("replaces_txid", wtx.m_replaces_txid->ToString());74if (wtx.m_replaced_by_txid) entry.pushKV("replaced_by_txid", wtx.m_replaced_by_txid->ToString());75}77struct tallyitem78{79CAmount nAmount{0};80int nConf{std::numeric_limits<int>::max()};81std::vector<Txid> txids;82tallyitem() = default;83};85static UniValue ListReceived(const CWallet& wallet, const UniValue& params, const bool by_label, const bool include_immature_coinbase) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)86{87// Minimum confirmations88int nMinDepth = 1;89if (!params[0].isNull())90nMinDepth = params[0].getInt<int>();92// Whether to include empty labels93bool fIncludeEmpty = false;94if (!params[1].isNull())95fIncludeEmpty = params[1].get_bool();97std::optional<CTxDestination> filtered_address{std::nullopt};98if (!by_label && !params[3].isNull() && !params[3].get_str().empty()) {99if (!IsValidDestinationString(params[3].get_str())) {100throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");101}102filtered_address = DecodeDestination(params[3].get_str());103}105// Tally106std::map<CTxDestination, tallyitem> mapTally;107for (const auto& [_, wtx] : wallet.mapWallet) {109int nDepth = wallet.GetTxDepthInMainChain(wtx);110if (nDepth < nMinDepth)111continue;113// Coinbase with less than 1 confirmation is no longer in the main chain114if ((wtx.IsCoinBase() && (nDepth < 1))115|| (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) {116continue;117}119for (const CTxOut& txout : wtx.GetTx()->vout) {120CTxDestination address;121if (!ExtractDestination(txout.scriptPubKey, address))122continue;124if (filtered_address && !(filtered_address == address)) {125continue;126}128if (!wallet.IsMine(address))129continue;131tallyitem& item = mapTally[address];132item.nAmount += txout.nValue;133item.nConf = std::min(item.nConf, nDepth);134item.txids.push_back(wtx.GetHash());135}136}138// Reply139UniValue ret(UniValue::VARR);140std::map<std::string, tallyitem> label_tally;142const auto& func = [&](const CTxDestination& address, const std::string& label, bool is_change,143const std::optional<AddressPurpose>& purpose) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) {144if (is_change) return; // no change addresses146// Entries in mapTally are only ever added for wallet.IsMine() addresses (see the tally147// loop above), so it's only addresses missing from mapTally that need the IsMine() check.148auto it = mapTally.find(address);149if (it == mapTally.end()) {150if (!fIncludeEmpty) return;151if (!wallet.IsMine(address)) return; // exclude addresses not owned by the wallet (e.g. "send" purpose)152}154CAmount nAmount = 0;155int nConf = std::numeric_limits<int>::max();156if (it != mapTally.end()) {157nAmount = (*it).second.nAmount;158nConf = (*it).second.nConf;159}161if (by_label) {162tallyitem& _item = label_tally[label];163_item.nAmount += nAmount;164_item.nConf = std::min(_item.nConf, nConf);165} else {166UniValue obj(UniValue::VOBJ);167obj.pushKV("address", EncodeDestination(address));168obj.pushKV("amount", ValueFromAmount(nAmount));169obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));170obj.pushKV("label", label);171UniValue transactions(UniValue::VARR);172if (it != mapTally.end()) {173for (const Txid& _item : (*it).second.txids) {174transactions.push_back(_item.GetHex());175}176}177obj.pushKV("txids", std::move(transactions));178ret.push_back(std::move(obj));179}180};182if (filtered_address) {183const auto& entry = wallet.FindAddressBookEntry(*filtered_address, /*allow_change=*/false);184if (entry) func(*filtered_address, entry->GetLabel(), entry->IsChange(), entry->purpose);185} else {186// No filtered addr, walk-through the addressbook entry187wallet.ForEachAddrBookEntry(func);188}190if (by_label) {191for (const auto& entry : label_tally) {192CAmount nAmount = entry.second.nAmount;193int nConf = entry.second.nConf;194UniValue obj(UniValue::VOBJ);195obj.pushKV("amount", ValueFromAmount(nAmount));196obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));197obj.pushKV("label", entry.first);198ret.push_back(std::move(obj));199}200}202return ret;203}205RPCMethod listreceivedbyaddress()206{207return RPCMethod{208"listreceivedbyaddress",209"List balances by receiving address.\n",210{211{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},212{"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},213{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},214{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If present and non-empty, only return information on this address."},215{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},216},217RPCResult{218RPCResult::Type::ARR, "", "",219{220{RPCResult::Type::OBJ, "", "",221{222{RPCResult::Type::STR, "address", "The receiving address"},223{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},224{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},225{RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},226{RPCResult::Type::ARR, "txids", "",227{228{RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},229}},230}},231}232},233RPCExamples{234HelpExampleCli("listreceivedbyaddress", "")235+ HelpExampleCli("listreceivedbyaddress", "6 true")236+ HelpExampleCli("listreceivedbyaddress", "6 true true \"\" true")237+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")238+ HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\", true")239},240[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue241{242const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);243if (!pwallet) return UniValue::VNULL;245// Make sure the results are valid at least up to the most recent block246// the user could have gotten from another RPC command prior to now247pwallet->BlockUntilSyncedToCurrentChain();249const bool include_immature_coinbase{request.params[4].isNull() ? false : request.params[4].get_bool()};