mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-28 13:02:38 +02:00
Merge 22693 via getaddressinfo_txids-26
This commit is contained in:
commit
9c899a42c9
@ -538,6 +538,10 @@ RPCHelpMan getaddressinfo()
|
||||
{
|
||||
{RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
|
||||
}},
|
||||
{RPCResult::Type::ARR, "use_txids", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "txid", "The ids of transactions involving this wallet which received with the address"},
|
||||
}},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
@ -631,6 +635,15 @@ RPCHelpMan getaddressinfo()
|
||||
}
|
||||
ret.pushKV("labels", std::move(labels));
|
||||
|
||||
// NOTE: Intentionally not special-casing a single txid: while addresses
|
||||
// should never be reused, it's not unexpected to have RBF result in
|
||||
// multiple txids for a single use.
|
||||
UniValue use_txids(UniValue::VARR);
|
||||
pwallet->FindScriptPubKeyUsed(std::set<CScript>{scriptPubKey}, [&use_txids](const CWalletTx&wtx) {
|
||||
use_txids.push_back(wtx.GetHash().GetHex());
|
||||
});
|
||||
ret.pushKV("use_txids", std::move(use_txids));
|
||||
|
||||
return ret;
|
||||
},
|
||||
};
|
||||
|
@ -786,6 +786,60 @@ void CWallet::AddToSpends(const CWalletTx& wtx, WalletBatch* batch)
|
||||
AddToSpends(txin.prevout, wtx.GetHash(), batch);
|
||||
}
|
||||
|
||||
void CWallet::InitialiseAddressBookUsed()
|
||||
{
|
||||
for (const auto& entry : mapWallet) {
|
||||
const CWalletTx& wtx = entry.second;
|
||||
UpdateAddressBookUsed(wtx);
|
||||
}
|
||||
}
|
||||
|
||||
void CWallet::UpdateAddressBookUsed(const CWalletTx& wtx)
|
||||
{
|
||||
for (const auto& output : wtx.tx->vout) {
|
||||
CTxDestination dest;
|
||||
if (!ExtractDestination(output.scriptPubKey, dest)) continue;
|
||||
m_address_book[dest].m_used = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CWallet::FindScriptPubKeyUsed(const std::set<CScript>& keys, const std::variant<std::monostate, std::function<void(const CWalletTx&)>, std::function<void(const CWalletTx&, uint32_t)>>& callback) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
bool found_any = false;
|
||||
for (const auto& key : keys) {
|
||||
CTxDestination dest;
|
||||
if (!ExtractDestination(key, dest)) continue;
|
||||
const auto& address_book_it = m_address_book.find(dest);
|
||||
if (address_book_it == m_address_book.end()) continue;
|
||||
if (address_book_it->second.m_used) {
|
||||
found_any = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_any) return false;
|
||||
if (std::holds_alternative<std::monostate>(callback)) return true;
|
||||
|
||||
found_any = false;
|
||||
for (const auto& entry : mapWallet) {
|
||||
const CWalletTx& wtx = entry.second;
|
||||
for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||
const auto& output = wtx.tx->vout[i];
|
||||
if (keys.count(output.scriptPubKey)) {
|
||||
found_any = true;
|
||||
const auto callback_type = callback.index();
|
||||
if (callback_type == 1) {
|
||||
std::get<std::function<void(const CWalletTx&)>>(callback)(wtx);
|
||||
break;
|
||||
}
|
||||
std::get<std::function<void(const CWalletTx&, uint32_t)>>(callback)(wtx, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_any;
|
||||
}
|
||||
|
||||
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||
{
|
||||
if (IsCrypted())
|
||||
@ -1083,6 +1137,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
|
||||
|
||||
// Update birth time when tx time is older than it.
|
||||
MaybeUpdateBirthTime(wtx.GetTxTime());
|
||||
UpdateAddressBookUsed(wtx);
|
||||
}
|
||||
|
||||
if (!fInsertedNew)
|
||||
@ -2311,7 +2366,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
|
||||
}
|
||||
}
|
||||
|
||||
DBErrors CWallet::LoadWallet()
|
||||
DBErrors CWallet::LoadWallet(const do_init_used_flag do_init_used_flag_val)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
@ -2331,7 +2386,13 @@ DBErrors CWallet::LoadWallet()
|
||||
assert(m_internal_spk_managers.empty());
|
||||
}
|
||||
|
||||
return nLoadWalletRet;
|
||||
if (nLoadWalletRet != DBErrors::LOAD_OK) {
|
||||
return nLoadWalletRet;
|
||||
}
|
||||
|
||||
if (do_init_used_flag_val == do_init_used_flag::Init) InitialiseAddressBookUsed();
|
||||
|
||||
return DBErrors::LOAD_OK;
|
||||
}
|
||||
|
||||
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/signals2/signal.hpp>
|
||||
@ -237,6 +238,12 @@ struct CAddressBookData
|
||||
*/
|
||||
std::optional<std::string> label;
|
||||
|
||||
/** Whether address is the destination of any wallet transation.
|
||||
* Unlike other fields in address data struct, the used value is determined
|
||||
* at runtime and not serialized as part of address data.
|
||||
*/
|
||||
bool m_used{false};
|
||||
|
||||
/**
|
||||
* Address purpose which was originally recorded for payment protocol
|
||||
* support but now serves as a cached IsMine value. Wallet code should
|
||||
@ -337,6 +344,9 @@ private:
|
||||
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void AddToSpends(const CWalletTx& wtx, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
void InitialiseAddressBookUsed() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void UpdateAddressBookUsed(const CWalletTx&) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
/**
|
||||
* Add a transaction to the wallet, or update it. confirm.block_* should
|
||||
* be set when the transaction was known to be included in a block. When
|
||||
@ -537,6 +547,8 @@ public:
|
||||
bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
bool FindScriptPubKeyUsed(const std::set<CScript>& keys, const std::variant<std::monostate, std::function<void(const CWalletTx&)>, std::function<void(const CWalletTx&, uint32_t)>>& callback = std::monostate()) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
/*
|
||||
* Rescan abort properties
|
||||
*/
|
||||
@ -781,7 +793,8 @@ public:
|
||||
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
|
||||
void chainStateFlushed(ChainstateRole role, const CBlockLocator& loc) override;
|
||||
|
||||
DBErrors LoadWallet();
|
||||
enum class do_init_used_flag { Init, Skip };
|
||||
DBErrors LoadWallet(const do_init_used_flag do_init_used_flag_val = do_init_used_flag::Init);
|
||||
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& purpose);
|
||||
|
@ -47,7 +47,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
|
||||
wallet_instance->TopUpKeyPool();
|
||||
}
|
||||
|
||||
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
|
||||
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options, CWallet::do_init_used_flag do_init_used_flag_val = CWallet::do_init_used_flag::Init)
|
||||
{
|
||||
DatabaseStatus status;
|
||||
bilingual_str error;
|
||||
@ -61,7 +61,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
|
||||
std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet};
|
||||
DBErrors load_wallet_ret;
|
||||
try {
|
||||
load_wallet_ret = wallet_instance->LoadWallet();
|
||||
load_wallet_ret = wallet_instance->LoadWallet(do_init_used_flag_val);
|
||||
} catch (const std::runtime_error&) {
|
||||
tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name);
|
||||
return nullptr;
|
||||
@ -168,7 +168,8 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
|
||||
DatabaseOptions options;
|
||||
ReadDatabaseArgs(args, options);
|
||||
options.require_existing = true;
|
||||
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
|
||||
// NOTE: We need to skip initialisation of the m_used flag, or else the address book count might be wrong
|
||||
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options, CWallet::do_init_used_flag::Skip);
|
||||
if (!wallet_instance) return false;
|
||||
WalletShowInfo(wallet_instance.get());
|
||||
wallet_instance->Close();
|
||||
|
@ -655,6 +655,16 @@ class WalletTest(BitcoinTestFramework):
|
||||
assert not address_info["iswatchonly"]
|
||||
assert not address_info["isscript"]
|
||||
assert not address_info["ischange"]
|
||||
assert_equal(address_info['use_txids'], [])
|
||||
|
||||
# Test getaddressinfo 'use_txids' field
|
||||
addr = "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ"
|
||||
txid_1 = self.nodes[0].sendtoaddress(addr, 1)
|
||||
address_info = self.nodes[0].getaddressinfo(addr)
|
||||
assert_equal(address_info['use_txids'], [txid_1])
|
||||
txid_2 = self.nodes[0].sendtoaddress(addr, 1)
|
||||
address_info = self.nodes[0].getaddressinfo(addr)
|
||||
assert_equal(sorted(address_info['use_txids']), sorted([txid_1, txid_2]))
|
||||
|
||||
# Test getaddressinfo 'ischange' field on change address.
|
||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
|
||||
|
Loading…
Reference in New Issue
Block a user