Merge #19289: wallet: GetWalletTx and IsMine require cs_wallet lock

b8405b833a wallet: IsChange requires cs_wallet lock (João Barbosa)
d8441f30ff wallet: IsMine overloads require cs_wallet lock (João Barbosa)
a13cafc6c6 wallet: GetWalletTx requires cs_wallet lock (João Barbosa)

Pull request description:

  This change removes some unlock/lock and lock/lock cases regarding `GetWalletTx` and `IsMine` overloads.

ACKs for top commit:
  laanwj:
    Code review ACK b8405b833a
  ryanofsky:
    Code review ACK b8405b833a. Just new commit since last review changing IsChange GetChange locks and adding annotations

Tree-SHA512: 40d37c4fe5d10a1407f57d899d5822bb285633d8dbfad8afcf15a9b41b428ed9971a9a7b1aae84318371155132df3002699a15dab56e004527d50c889829187d
This commit is contained in:
Wladimir J. van der Laan 2020-08-27 16:21:32 +02:00
commit 91af7ef831
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
3 changed files with 45 additions and 32 deletions

View file

@ -37,6 +37,7 @@ namespace {
//! Construct wallet tx struct. //! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{ {
LOCK(wallet.cs_wallet);
WalletTx result; WalletTx result;
result.tx = wtx.tx; result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size()); result.txin_is_mine.reserve(wtx.tx->vin.size());
@ -132,7 +133,11 @@ public:
{ {
return m_wallet->SignMessage(message, pkhash, str_sig); return m_wallet->SignMessage(message, pkhash, str_sig);
} }
bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } bool isSpendable(const CTxDestination& dest) override
{
LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
}
bool haveWatchOnly() override bool haveWatchOnly() override
{ {
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan(); auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();

View file

@ -276,7 +276,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{ {
LOCK(cs_wallet); AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end()) if (it == mapWallet.end())
return nullptr; return nullptr;
@ -1210,15 +1210,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
isminetype CWallet::IsMine(const CTxIn &txin) const isminetype CWallet::IsMine(const CTxIn &txin) const
{ {
AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{ {
LOCK(cs_wallet); const CWalletTx& prev = (*mi).second;
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); if (txin.prevout.n < prev.tx->vout.size())
if (mi != mapWallet.end()) return IsMine(prev.tx->vout[txin.prevout.n]);
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.tx->vout.size())
return IsMine(prev.tx->vout[txin.prevout.n]);
}
} }
return ISMINE_NO; return ISMINE_NO;
} }
@ -1243,16 +1241,19 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
isminetype CWallet::IsMine(const CTxOut& txout) const isminetype CWallet::IsMine(const CTxOut& txout) const
{ {
AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey); return IsMine(txout.scriptPubKey);
} }
isminetype CWallet::IsMine(const CTxDestination& dest) const isminetype CWallet::IsMine(const CTxDestination& dest) const
{ {
AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest)); return IsMine(GetScriptForDestination(dest));
} }
isminetype CWallet::IsMine(const CScript& script) const isminetype CWallet::IsMine(const CScript& script) const
{ {
AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO; isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) { for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script)); result = std::max(result, spk_man_pair.second->IsMine(script));
@ -1264,6 +1265,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{ {
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
LOCK(cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0); return ((IsMine(txout) & filter) ? txout.nValue : 0);
} }
@ -1281,13 +1283,12 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are // a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember // 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change). // which output, if any, was change).
AssertLockHeld(cs_wallet);
if (IsMine(script)) if (IsMine(script))
{ {
CTxDestination address; CTxDestination address;
if (!ExtractDestination(script, address)) if (!ExtractDestination(script, address))
return true; return true;
LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) { if (!FindAddressBookEntry(address)) {
return true; return true;
} }
@ -1297,6 +1298,7 @@ bool CWallet::IsChange(const CScript& script) const
CAmount CWallet::GetChange(const CTxOut& txout) const CAmount CWallet::GetChange(const CTxOut& txout) const
{ {
AssertLockHeld(cs_wallet);
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0); return (IsChange(txout) ? txout.nValue : 0);
@ -1304,6 +1306,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const bool CWallet::IsMine(const CTransaction& tx) const
{ {
AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
if (IsMine(txout)) if (IsMine(txout))
return true; return true;
@ -1362,6 +1365,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const CAmount CWallet::GetChange(const CTransaction& tx) const
{ {
LOCK(cs_wallet);
CAmount nChange = 0; CAmount nChange = 0;
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
{ {
@ -1597,6 +1601,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
LOCK(pwallet->cs_wallet);
// Sent/received. // Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i) for (unsigned int i = 0; i < tx->vout.size(); ++i)
{ {
@ -1983,6 +1988,7 @@ bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
if (!InMempool()) return false; if (!InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool: // Trusted if all inputs are from us and are in the mempool:
LOCK(pwallet->cs_wallet);
for (const CTxIn& txin : tx->vin) for (const CTxIn& txin : tx->vin)
{ {
// Transactions not sent by us: not trusted // Transactions not sent by us: not trusted
@ -3194,6 +3200,7 @@ DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose) bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{ {
bool fUpdated = false; bool fUpdated = false;
bool is_mine;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address); std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@ -3201,8 +3208,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName); m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */ if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose; m_address_book[address].purpose = strPurpose;
is_mine = IsMine(address) != ISMINE_NO;
} }
NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO, NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose)) if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false; return false;
@ -3217,17 +3225,16 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address) bool CWallet::DelAddressBook(const CTxDestination& address)
{ {
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted) bool is_mine;
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
if (IsMine(address)) {
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
return false;
}
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
if (IsMine(address)) {
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
return false;
}
// Delete destdata tuples associated with address // Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address); std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata) for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
@ -3235,9 +3242,10 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
WalletBatch(*database).EraseDestData(strAddress, item.first); WalletBatch(*database).EraseDestData(strAddress, item.first);
} }
m_address_book.erase(address); m_address_book.erase(address);
is_mine = IsMine(address) != ISMINE_NO;
} }
NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED); NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
WalletBatch(*database).ErasePurpose(EncodeDestination(address)); WalletBatch(*database).ErasePurpose(EncodeDestination(address));
return WalletBatch(*database).EraseName(EncodeDestination(address)); return WalletBatch(*database).EraseName(EncodeDestination(address));

View file

@ -805,7 +805,7 @@ public:
/** Interface for accessing chain state. */ /** Interface for accessing chain state. */
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; } interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const; const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we are allowed to upgrade (or already support) to the named feature //! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
@ -1051,20 +1051,20 @@ public:
bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error); bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error); bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
isminetype IsMine(const CTxDestination& dest) const; isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CScript& script) const; isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CTxIn& txin) const; isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Returns amount of debit if the input matches the * Returns amount of debit if the input matches the
* filter, otherwise returns 0 * filter, otherwise returns 0
*/ */
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const; isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
bool IsChange(const CTxOut& txout) const; bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsChange(const CScript& script) const; bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetChange(const CTxOut& txout) const; CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsMine(const CTransaction& tx) const; bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */ /** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const; bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;