diff options
author | W. J. van der Laan <laanwj@protonmail.com> | 2021-12-02 18:33:10 +0100 |
---|---|---|
committer | W. J. van der Laan <laanwj@protonmail.com> | 2021-12-02 19:37:33 +0100 |
commit | bce58bbb3d7b7351acd4c988748ba73908c60119 (patch) | |
tree | 049b77f0303a980b5ef09e9b77d700318d136aa6 /src/wallet/wallet.cpp | |
parent | 26a1147ce56083d7aa820ac115c16b01e47d911c (diff) | |
parent | 3d71d16d1eb4173c70d4c294559fc2365e189856 (diff) |
Merge bitcoin/bitcoin#22929: wallet: Automatically add receiving destinations to the address book
3d71d16d1eb4173c70d4c294559fc2365e189856 test: listtranscations with externally generated addresses (S3RK)
d04566415e16ae685af066384f346dff522c068f Add to spends only transcations from me (S3RK)
9f3a622b1cea37e452560f2f82d8e82d3b48a73a Automatically add labels to detected receiving addresses (S3RK)
c1b99c088c54eb101c0a28a67237965576ccf5ad Return used destinations from ScriptPubKeyMan::MarkUnusedAddresses (S3RK)
03840c20640685295a65ed8c82456e877f668b9b Add CWallet::IsInternalScriptPubKeyMan (S3RK)
456e350926adde5dabdbc85fc0f017fb29bdadb3 wallet: resolve ambiguity of two ScriptPubKey managers providing same script (S3RK)
Pull request description:
This PR fixes certain use-cases when **send-to-self** transactions are missing from `listtransactions` output.
1. When a receiving address is generated externally to the wallet
(e.g. same wallet running on two nodes, or by 3rd party from xpub)
2. When restoring backup with lost metadata, but keypool gap is not exceeded yet
When the block is connected or tx added to mempool we already mark used keys. This PR extends this logic to determine whether the destination is a receiving one and if yes add it to the address book with empty label.
Works both for legacy and descriptors wallets.
- For legacy it uses the internal flag from the keypool entry. Caveat: because we don't know which script type would be used we add all possible destinations for such keys.
- For descriptor wallets it uses internal flag for the script pub key manager. Caveat: it only works for active descriptors.
fixes #19856
fixes #20293
ACKs for top commit:
laanwj:
Code review ACK 3d71d16d1eb4173c70d4c294559fc2365e189856
Tree-SHA512: 03fafd5548ead0c4ffe9ebcc9eb2849f1d2fa7270fda4166419b86877d4e57dcf04460e465fbb9c90b42031f3c05d1b83f1b67a9f82c2a42980825ed1e7b52e6
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 78 |
1 files changed, 54 insertions, 24 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e4c3822305..2e4b6a6eb0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -919,7 +919,9 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const wtx.nOrderPos = IncOrderPosNext(&batch); wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block); - AddToSpends(hash, &batch); + if (IsFromMe(*tx.get())) { + AddToSpends(hash); + } } if (!fInsertedNew) @@ -1061,8 +1063,23 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxS // loop though all outputs for (const CTxOut& txout: tx.vout) { - for (const auto& spk_man_pair : m_spk_managers) { - spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey); + for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) { + for (auto &dest : spk_man->MarkUnusedAddresses(txout.scriptPubKey)) { + // If internal flag is not defined try to infer it from the ScriptPubKeyMan + if (!dest.internal.has_value()) { + dest.internal = IsInternalScriptPubKeyMan(spk_man); + } + + // skip if can't determine whether it's a receiving address or not + if (!dest.internal.has_value()) continue; + + // If this is a receiving address and it's not in the address book yet + // (e.g. it wasn't generated on this node or we're restoring from backup) + // add it to the address book for proper transaction accounting + if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) { + SetAddressBook(dest.dest, "", "receive"); + } + } } } @@ -2253,16 +2270,15 @@ void ReserveDestination::ReturnDestination() bool CWallet::DisplayAddress(const CTxDestination& dest) { CScript scriptPubKey = GetScriptForDestination(dest); - const auto spk_man = GetScriptPubKeyMan(scriptPubKey); - if (spk_man == nullptr) { - return false; - } - auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man); - if (signer_spk_man == nullptr) { - return false; + for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) { + auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man); + if (signer_spk_man == nullptr) { + continue; + } + ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); + return signer_spk_man->DisplayAddress(scriptPubKey, signer); } - ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); - return signer_spk_man->DisplayAddress(scriptPubKey, signer); + return false; } bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch) @@ -3050,9 +3066,10 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern return it->second; } -std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const +std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const { std::set<ScriptPubKeyMan*> spk_mans; + SignatureData sigdata; for (const auto& spk_man_pair : m_spk_managers) { if (spk_man_pair.second->CanProvide(script, sigdata)) { spk_mans.insert(spk_man_pair.second.get()); @@ -3061,17 +3078,6 @@ std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, S return spk_mans; } -ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const -{ - SignatureData sigdata; - for (const auto& spk_man_pair : m_spk_managers) { - if (spk_man_pair.second->CanProvide(script, sigdata)) { - return spk_man_pair.second.get(); - } - } - return nullptr; -} - ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const { if (m_spk_managers.count(id) > 0) { @@ -3287,6 +3293,30 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes return nullptr; } +std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const +{ + // Legacy script pubkey man can't be either external or internal + if (IsLegacy()) { + return std::nullopt; + } + + // only active ScriptPubKeyMan can be internal + if (!GetActiveScriptPubKeyMans().count(spk_man)) { + return std::nullopt; + } + + const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man); + if (!desc_spk_man) { + throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type."); + } + + LOCK(desc_spk_man->cs_desc_man); + const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType(); + assert(type.has_value()); + + return GetScriptPubKeyMan(*type, /* internal= */ true) == desc_spk_man; +} + ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) { AssertLockHeld(cs_wallet); |