merge bitcoin#15931: Remove GetDepthInMainChain dependency on locked chain interface

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
Kittywhiskers Van Gogh 2022-04-03 16:39:58 +05:30
parent ea3eefd30c
commit b735422a83
14 changed files with 291 additions and 222 deletions

View File

@ -56,12 +56,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
} }
return nullopt; return nullopt;
} }
int getBlockDepth(const uint256& hash) override
{
const Optional<int> tip_height = getHeight();
const Optional<int> height = getBlockHeight(hash);
return tip_height && height ? *tip_height - *height + 1 : 0;
}
uint256 getBlockHash(int height) override uint256 getBlockHash(int height) override
{ {
LockAnnotation lock(::cs_main); LockAnnotation lock(::cs_main);
@ -180,11 +174,11 @@ public:
const CBlockIndex* index, const CBlockIndex* index,
const std::vector<CTransactionRef>& tx_conflicted) override const std::vector<CTransactionRef>& tx_conflicted) override
{ {
m_notifications->BlockConnected(*block, tx_conflicted); m_notifications->BlockConnected(*block, tx_conflicted, index->nHeight);
} }
void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindexDisconnected) override void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{ {
m_notifications->BlockDisconnected(*block); m_notifications->BlockDisconnected(*block, index->nHeight);
} }
void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override
{ {
@ -348,13 +342,11 @@ public:
{ {
return MakeUnique<NotificationsHandlerImpl>(*this, notifications); return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
} }
void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) override void waitForNotificationsIfTipChanged(const uint256& old_tip) override
{ {
if (!old_tip.IsNull()) { if (!old_tip.IsNull()) {
LOCK(::cs_main); LOCK(::cs_main);
if (old_tip == ::chainActive.Tip()->GetBlockHash()) return; if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return;
CBlockIndex* block = LookupBlockIndex(old_tip);
if (block && block->GetAncestor(::chainActive.Height()) == ::chainActive.Tip()) return;
} }
SyncWithValidationInterfaceQueue(); SyncWithValidationInterfaceQueue();
} }

View File

@ -86,10 +86,6 @@ public:
//! included in the current chain. //! included in the current chain.
virtual Optional<int> getBlockHeight(const uint256& hash) = 0; virtual Optional<int> getBlockHeight(const uint256& hash) = 0;
//! Get block depth. Returns 1 for chain tip, 2 for preceding block, and
//! so on. Returns 0 for a block not included in the current chain.
virtual int getBlockDepth(const uint256& hash) = 0;
//! Get block hash. Height must be valid or this function will abort. //! Get block hash. Height must be valid or this function will abort.
virtual uint256 getBlockHash(int height) = 0; virtual uint256 getBlockHash(int height) = 0;
@ -231,8 +227,8 @@ public:
virtual ~Notifications() {} virtual ~Notifications() {}
virtual void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) {} virtual void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) {}
virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason) {} virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason) {}
virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted) {} virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted, int height) {}
virtual void BlockDisconnected(const CBlock& block) {} virtual void BlockDisconnected(const CBlock& block, int height) {}
virtual void UpdatedBlockTip() {} virtual void UpdatedBlockTip() {}
virtual void ChainStateFlushed(const CBlockLocator& locator) {} virtual void ChainStateFlushed(const CBlockLocator& locator) {}
virtual void NotifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const llmq::CChainLockSig>& clsig) {} virtual void NotifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const llmq::CChainLockSig>& clsig) {}
@ -243,9 +239,8 @@ public:
virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0; virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0;
//! Wait for pending notifications to be processed unless block hash points to the current //! Wait for pending notifications to be processed unless block hash points to the current
//! chain tip, or to a possible descendant of the current chain tip that isn't currently //! chain tip.
//! connected. virtual void waitForNotificationsIfTipChanged(const uint256& old_tip) = 0;
virtual void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) = 0;
//! Register handler for RPC. Command is not copied, so reference //! Register handler for RPC. Command is not copied, so reference
//! needs to remain valid until Handler is disconnected. //! needs to remain valid until Handler is disconnected.

View File

@ -32,7 +32,7 @@ namespace interfaces {
namespace { namespace {
//! Construct wallet tx struct. //! Construct wallet tx struct.
WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, const CWalletTx& wtx) WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{ {
WalletTx result; WalletTx result;
bool fInputDenomFound{false}, fOutputDenomFound{false}; bool fInputDenomFound{false}, fOutputDenomFound{false};
@ -57,7 +57,7 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
fOutputDenomFound = true; fOutputDenomFound = true;
} }
} }
result.credit = wtx.GetCredit(locked_chain, ISMINE_ALL); result.credit = wtx.GetCredit(ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL); result.debit = wtx.GetDebit(ISMINE_ALL);
result.change = wtx.GetChange(); result.change = wtx.GetChange();
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
@ -76,23 +76,22 @@ WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const C
{ {
WalletTxStatus result; WalletTxStatus result;
result.block_height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock).get_value_or(std::numeric_limits<int>::max()); result.block_height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock).get_value_or(std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain); result.blocks_to_maturity = wtx.GetBlocksToMaturity();
result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain); result.depth_in_main_chain = wtx.GetDepthInMainChain();
result.time_received = wtx.nTimeReceived; result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime; result.lock_time = wtx.tx->nLockTime;
result.is_final = locked_chain.checkFinalTx(*wtx.tx); result.is_final = locked_chain.checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(locked_chain); result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned(); result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase(); result.is_coinbase = wtx.IsCoinBase();
result.is_in_main_chain = wtx.IsInMainChain(locked_chain); result.is_in_main_chain = wtx.IsInMainChain();
result.is_chainlocked = wtx.IsChainLocked(); result.is_chainlocked = wtx.IsChainLocked();
result.is_islocked = wtx.IsLockedByInstantSend(); result.is_islocked = wtx.IsLockedByInstantSend();
return result; return result;
} }
//! Construct wallet TxOut struct. //! Construct wallet TxOut struct.
WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain, WalletTxOut MakeWalletTxOut(CWallet& wallet,
CWallet& wallet,
const CWalletTx& wtx, const CWalletTx& wtx,
int n, int n,
int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@ -101,7 +100,7 @@ WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
result.txout = wtx.tx->vout[n]; result.txout = wtx.tx->vout[n];
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
result.depth_in_main_chain = depth; result.depth_in_main_chain = depth;
result.is_spent = wallet.IsSpent(locked_chain, wtx.GetHash(), n); result.is_spent = wallet.IsSpent(wtx.GetHash(), n);
return result; return result;
} }
@ -302,7 +301,7 @@ public:
{ {
auto locked_chain = m_wallet->chain().lock(); auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
return m_wallet->AbandonTransaction(*locked_chain, txid); return m_wallet->AbandonTransaction(txid);
} }
CTransactionRef getTx(const uint256& txid) override CTransactionRef getTx(const uint256& txid) override
{ {
@ -320,7 +319,7 @@ public:
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid); auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) { if (mi != m_wallet->mapWallet.end()) {
return MakeWalletTx(*locked_chain, *m_wallet, mi->second); return MakeWalletTx(*m_wallet, mi->second);
} }
return {}; return {};
} }
@ -331,7 +330,7 @@ public:
std::vector<WalletTx> result; std::vector<WalletTx> result;
result.reserve(m_wallet->mapWallet.size()); result.reserve(m_wallet->mapWallet.size());
for (const auto& entry : m_wallet->mapWallet) { for (const auto& entry : m_wallet->mapWallet) {
result.emplace_back(MakeWalletTx(*locked_chain, *m_wallet, entry.second)); result.emplace_back(MakeWalletTx(*m_wallet, entry.second));
} }
return result; return result;
} }
@ -373,7 +372,7 @@ public:
in_mempool = mi->second.InMempool(); in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm; order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(*locked_chain, mi->second); tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
return MakeWalletTx(*locked_chain, *m_wallet, mi->second); return MakeWalletTx(*m_wallet, mi->second);
} }
return {}; return {};
} }
@ -477,7 +476,7 @@ public:
auto& group = result[entry.first]; auto& group = result[entry.first];
for (const auto& coin : entry.second) { for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i), group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
MakeWalletTxOut(*locked_chain, *m_wallet, *coin.tx, coin.i, coin.nDepth)); MakeWalletTxOut(*m_wallet, *coin.tx, coin.i, coin.nDepth));
} }
} }
return result; return result;
@ -492,9 +491,9 @@ public:
result.emplace_back(); result.emplace_back();
auto it = m_wallet->mapWallet.find(output.hash); auto it = m_wallet->mapWallet.find(output.hash);
if (it != m_wallet->mapWallet.end()) { if (it != m_wallet->mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(*locked_chain); int depth = it->second.GetDepthInMainChain();
if (depth >= 0) { if (depth >= 0) {
result.back() = MakeWalletTxOut(*locked_chain, *m_wallet, it->second, output.n, depth); result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth);
} }
} }
} }

View File

@ -114,9 +114,11 @@ void TestGUI()
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
{ {
auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive"); wallet->SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive");
wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
} }
{ {
auto locked_chain = wallet->chain().lock(); auto locked_chain = wallet->chain().lock();

View File

@ -37,10 +37,10 @@ struct TestSubscriber : public CValidationInterface {
m_expected_tip = block->GetHash(); m_expected_tip = block->GetHash();
} }
void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindexDisconnected) override void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
{ {
BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash()); BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
BOOST_CHECK_EQUAL(m_expected_tip, pindexDisconnected->GetBlockHash()); BOOST_CHECK_EQUAL(m_expected_tip, pindex->GetBlockHash());
m_expected_tip = block->hashPrevBlock; m_expected_tip = block->hashPrevBlock;
} }

View File

@ -41,7 +41,7 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> SynchronousUpdatedBlockTip; boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> SynchronousUpdatedBlockTip;
boost::signals2::signal<void (const CTransactionRef &, int64_t)> TransactionAddedToMempool; boost::signals2::signal<void (const CTransactionRef &, int64_t)> TransactionAddedToMempool;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected; boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex* pindexDisconnected)> BlockDisconnected; boost::signals2::signal<void (const std::shared_ptr<const CBlock>&, const CBlockIndex* pindex)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &, MemPoolRemovalReason)> TransactionRemovedFromMempool; boost::signals2::signal<void (const CTransactionRef &, MemPoolRemovalReason)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed; boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked; boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
@ -190,9 +190,9 @@ void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, c
}); });
} }
void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex* pindexDisconnected) { void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex* pindex) {
m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindexDisconnected, this] { m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, this] {
m_internals->BlockDisconnected(pblock, pindexDisconnected); m_internals->BlockDisconnected(pblock, pindex);
}); });
} }

View File

@ -131,7 +131,7 @@ protected:
* *
* Called on a background thread. * Called on a background thread.
*/ */
virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindexDisconnected) {} virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex) {}
virtual void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const llmq::CInstantSendLock>& islock) {} virtual void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const llmq::CInstantSendLock>& islock) {}
virtual void NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr<const llmq::CChainLockSig>& clsig) {} virtual void NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr<const llmq::CChainLockSig>& clsig) {}
virtual void NotifyGovernanceVote(const std::shared_ptr<const CGovernanceVote>& vote) {} virtual void NotifyGovernanceVote(const std::shared_ptr<const CGovernanceVote>& vote) {}
@ -205,7 +205,7 @@ public:
void SynchronousUpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload); void SynchronousUpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
void TransactionAddedToMempool(const CTransactionRef &, int64_t); void TransactionAddedToMempool(const CTransactionRef &, int64_t);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &); void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindexDisconnected); void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const llmq::CInstantSendLock>& islock); void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const llmq::CInstantSendLock>& islock);
void NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr<const llmq::CChainLockSig>& clsig); void NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr<const llmq::CChainLockSig>& clsig);
void NotifyGovernanceVote(const std::shared_ptr<const CGovernanceVote>& vote); void NotifyGovernanceVote(const std::shared_ptr<const CGovernanceVote>& vote);

View File

@ -314,7 +314,7 @@ UniValue importaddress(const JSONRPCRequest& request)
{ {
auto locked_chain = pwallet->chain().lock(); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions(*locked_chain); pwallet->ReacceptWalletTransactions();
} }
} }
@ -353,30 +353,26 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
//Search partial merkle tree in proof for our transaction and index in valid block //Search partial merkle tree in proof for our transaction and index in valid block
std::vector<uint256> vMatch; std::vector<uint256> vMatch;
std::vector<unsigned int> vIndex; std::vector<unsigned int> vIndex;
unsigned int txnIndex = 0; if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
auto locked_chain = pwallet->chain().lock();
LockAnnotation lock(::cs_main);
if (locked_chain->getBlockHeight(merkleBlock.header.GetHash()) == nullopt) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
std::vector<uint256>::const_iterator it;
if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx))==vMatch.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
}
txnIndex = vIndex[it - vMatch.begin()];
}
else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
} }
wtx.SetConf(CWalletTx::Status::CONFIRMED, merkleBlock.header.GetHash(), txnIndex);
auto locked_chain = pwallet->chain().lock(); auto locked_chain = pwallet->chain().lock();
LockAnnotation lock(::cs_main); Optional<int> height = locked_chain->getBlockHeight(merkleBlock.header.GetHash());
if (height == nullopt) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
std::vector<uint256>::const_iterator it;
if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
}
unsigned int txnIndex = vIndex[it - vMatch.begin()];
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *height, merkleBlock.header.GetHash(), txnIndex);
wtx.m_confirm = confirm;
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
if (pwallet->IsMine(*wtx.tx)) { if (pwallet->IsMine(*wtx.tx)) {
@ -498,7 +494,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
{ {
auto locked_chain = pwallet->chain().lock(); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions(*locked_chain); pwallet->ReacceptWalletTransactions();
} }
} }
@ -1589,7 +1585,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
{ {
auto locked_chain = pwallet->chain().lock(); auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
pwallet->ReacceptWalletTransactions(*locked_chain); pwallet->ReacceptWalletTransactions();
} }
if (pwallet->IsAbortingRescan()) { if (pwallet->IsAbortingRescan()) {

View File

@ -96,7 +96,7 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry) static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
{ {
AssertLockHeld(cs_main); // for mapBlockIndex AssertLockHeld(cs_main); // for mapBlockIndex
int confirms = wtx.GetDepthInMainChain(locked_chain); int confirms = wtx.GetDepthInMainChain();
bool fLocked = llmq::quorumInstantSendManager->IsLocked(wtx.GetHash()); bool fLocked = llmq::quorumInstantSendManager->IsLocked(wtx.GetHash());
bool chainlock = false; bool chainlock = false;
if (confirms > 0) { if (confirms > 0) {
@ -648,7 +648,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey) if (txout.scriptPubKey == scriptPubKey)
if ((wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) if ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend()))
nAmount += txout.nValue; nAmount += txout.nValue;
} }
@ -714,7 +714,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{ {
CTxDestination address; CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) { if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
if ((wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) if ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend()))
nAmount += txout.nValue; nAmount += txout.nValue;
} }
} }
@ -1080,7 +1080,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx))
continue; continue;
int nDepth = wtx.GetDepthInMainChain(locked_chain); int nDepth = wtx.GetDepthInMainChain();
if ((nDepth < nMinDepth) && !(fAddLocked && wtx.IsLockedByInstantSend())) if ((nDepth < nMinDepth) && !(fAddLocked && wtx.IsLockedByInstantSend()))
continue; continue;
@ -1344,7 +1344,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
} }
// Received // Received
if (listReceived.size() > 0 && ((wtx.GetDepthInMainChain(locked_chain) >= nMinDepth) || wtx.IsLockedByInstantSend())) if (listReceived.size() > 0 && ((wtx.GetDepthInMainChain() >= nMinDepth) || wtx.IsLockedByInstantSend()))
{ {
for (const COutputEntry& r : listReceived) for (const COutputEntry& r : listReceived)
{ {
@ -1362,9 +1362,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
MaybePushAddress(entry, r.destination); MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
{ {
if (wtx.GetDepthInMainChain(locked_chain) < 1) if (wtx.GetDepthInMainChain() < 1)
entry.pushKV("category", "orphan"); entry.pushKV("category", "orphan");
else if (wtx.IsImmatureCoinBase(locked_chain)) else if (wtx.IsImmatureCoinBase())
entry.pushKV("category", "immature"); entry.pushKV("category", "immature");
else else
entry.pushKV("category", "generate"); entry.pushKV("category", "generate");
@ -1612,7 +1612,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second; CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) { if (depth == -1 || tx.GetDepthInMainChain() < depth) {
ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */); ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
} }
} }
@ -1723,7 +1723,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
} }
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
CAmount nCredit = wtx.GetCredit(*locked_chain, filter); CAmount nCredit = wtx.GetCredit(filter);
CAmount nDebit = wtx.GetDebit(filter); CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
@ -1782,7 +1782,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
if (!pwallet->mapWallet.count(hash)) { if (!pwallet->mapWallet.count(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
} }
if (!pwallet->AbandonTransaction(*locked_chain, hash)) { if (!pwallet->AbandonTransaction(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
} }
@ -2228,7 +2228,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
} }
if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) { if (pwallet->IsSpent(outpt.hash, outpt.n)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
} }

View File

@ -8,6 +8,7 @@
#include <coinjoin/util.h> #include <coinjoin/util.h>
#include <coinjoin/coinjoin.h> #include <coinjoin/coinjoin.h>
#include <coinjoin/options.h> #include <coinjoin/options.h>
#include <node/context.h>
#include <util/translation.h> #include <util/translation.h>
#include <validation.h> #include <validation.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
@ -73,6 +74,7 @@ public:
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
} }
WalletRescanReserver reserver(wallet.get()); WalletRescanReserver reserver(wallet.get());
reserver.reserve(); reserver.reserve();
@ -102,7 +104,9 @@ public:
CreateAndProcessBlock({blocktx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({blocktx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
auto locked_chain = wallet->chain().lock(); auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
it->second.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second; return it->second;
} }
CompactTallyItem GetTallyItem(const std::vector<CAmount>& vecAmounts) CompactTallyItem GetTallyItem(const std::vector<CAmount>& vecAmounts)

View File

@ -50,6 +50,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions accommodates a null start block. // Verify ScanForWalletTransactions accommodates a null start block.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -65,6 +69,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// and new block files. // and new block files.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -84,6 +92,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// file. // file.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -102,6 +114,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks. // Verify ScanForWalletTransactions scans no blocks.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey); AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet); WalletRescanReserver reserver(&wallet);
reserver.reserve(); reserver.reserve();
@ -250,18 +266,20 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
auto locked_chain = chain->lock(); auto locked_chain = chain->lock();
LockAnnotation lock(::cs_main); LockAnnotation lock(::cs_main);
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
wtx.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0); CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to // Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0. // cache the current immature credit amount, which is 0.
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0);
// Invalidate the cached value, add the key, and make sure a new immature // Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated. // credit amount is calculated.
wtx.MarkDirty(); wtx.MarkDirty();
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 500*COIN); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 500*COIN);
} }
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime) static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@ -292,7 +310,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
wallet.AddToWallet(wtx); wallet.AddToWallet(wtx);
} }
if (block) { if (block) {
wtx.SetConf(CWalletTx::Status::CONFIRMED, block->GetBlockHash(), 0); CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->nHeight, block->GetBlockHash(), 0);
wtx.m_confirm = confirm;
} }
wallet.AddToWallet(wtx); wallet.AddToWallet(wtx);
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart; return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
@ -347,6 +366,10 @@ public:
{ {
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase()); wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
{
LOCK(wallet->cs_wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey); AddKey(*wallet, coinbaseKey);
@ -385,9 +408,11 @@ public:
LOCK(cs_main); LOCK(cs_main);
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash()); auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end()); BOOST_CHECK(it != wallet->mapWallet.end());
it->second.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1); CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second; return it->second;
} }
@ -494,6 +519,10 @@ public:
AddKey(*wallet, coinbaseKey); AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get()); WalletRescanReserver reserver(wallet.get());
reserver.reserve(); reserver.reserve();
{
LOCK(wallet->cs_wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash() /* start_block */, {} /* stop_block */, reserver, false /* update */); CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash() /* start_block */, {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
} }
@ -594,7 +623,9 @@ public:
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
auto it = wallet->mapWallet.find(tx->GetHash()); auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end()); BOOST_CHECK(it != wallet->mapWallet.end());
it->second.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
std::vector<COutPoint> vecOutpoints; std::vector<COutPoint> vecOutpoints;
size_t n; size_t n;
@ -929,6 +960,11 @@ BOOST_FIXTURE_TEST_CASE(select_coins_grouped_by_addresses, ListCoinsTestingSetup
BOOST_CHECK_EQUAL(wallet->GetAvailableBalance(), 0); BOOST_CHECK_EQUAL(wallet->GetAvailableBalance(), 0);
CreateAndProcessBlock({CMutableTransaction(*tx2)}, GetScriptForRawPubKey({})); CreateAndProcessBlock({CMutableTransaction(*tx2)}, GetScriptForRawPubKey({}));
{
LOCK2(cs_main, wallet->cs_wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
// Reveal the mined tx, it should conflict with the one we have in the wallet already. // Reveal the mined tx, it should conflict with the one we have in the wallet already.
WalletRescanReserver reserver(wallet.get()); WalletRescanReserver reserver(wallet.get());
reserver.reserve(); reserver.reserve();

View File

@ -936,7 +936,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
* Outpoint is spent if any non-conflicted transaction * Outpoint is spent if any non-conflicted transaction
* spends it: * spends it:
*/ */
bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{ {
const COutPoint outpoint(hash, n); const COutPoint outpoint(hash, n);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
@ -947,7 +947,7 @@ bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash
const uint256& wtxid = it->second; const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) { if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain(locked_chain); int depth = mit->second.GetDepthInMainChain();
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent return true; // Spent
} }
@ -1204,7 +1204,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) { for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
if (IsMine(wtx.tx->vout[i]) && !IsSpent(*chain().lock(), hash, i)) { if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
setWalletUTXO.insert(COutPoint(hash, i)); setWalletUTXO.insert(COutPoint(hash, i));
if (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i))) { if (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i))) {
LockCoin(COutPoint(hash, i)); LockCoin(COutPoint(hash, i));
@ -1220,10 +1220,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.m_confirm.status = wtxIn.m_confirm.status; wtx.m_confirm.status = wtxIn.m_confirm.status;
wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex; wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex;
wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock; wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock;
wtx.m_confirm.block_height = wtxIn.m_confirm.block_height;
fUpdated = true; fUpdated = true;
} else { } else {
assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex); assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex);
assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock); assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock);
assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height);
} }
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{ {
@ -1233,7 +1235,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
auto mnList = deterministicMNManager->GetListAtChainTip(); auto mnList = deterministicMNManager->GetListAtChainTip();
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) { for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
if (IsMine(wtx.tx->vout[i]) && !IsSpent(*chain().lock(), hash, i)) { if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {
bool new_utxo = setWalletUTXO.insert(COutPoint(hash, i)).second; bool new_utxo = setWalletUTXO.insert(COutPoint(hash, i)).second;
if (new_utxo && (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i)))) { if (new_utxo && (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i)))) {
LockCoin(COutPoint(hash, i)); LockCoin(COutPoint(hash, i));
@ -1277,12 +1279,22 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
{ {
// If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken. // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
auto locked_chain = LockChain(); auto locked_chain = LockChain();
// If tx hasn't been reorged out of chain while wallet being shutdown if (locked_chain) {
// change tx status to UNCONFIRMED and reset hashBlock/nIndex. Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock);
if (!wtxIn.m_confirm.hashBlock.IsNull()) { if (block_height) {
if (locked_chain && !locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock)) { // Update cached block height variable since it not stored in the
// serialized transaction.
wtxIn.m_confirm.block_height = *block_height;
} else if (wtxIn.isConflicted() || wtxIn.isConfirmed()) {
// If tx block (or conflicting block) was reorged out of chain
// while the wallet was shutdown, change tx status to UNCONFIRMED
// and reset block height, hash, and index. ABANDONED tx don't have
// associated blocks and don't need to be updated. The case where a
// transaction was reorged out while online and then reconfirmed
// while offline is covered by the rescan logic.
wtxIn.setUnconfirmed(); wtxIn.setUnconfirmed();
wtxIn.m_confirm.hashBlock = uint256(); wtxIn.m_confirm.hashBlock = uint256();
wtxIn.m_confirm.block_height = 0;
wtxIn.m_confirm.nIndex = 0; wtxIn.m_confirm.nIndex = 0;
} }
} }
@ -1299,26 +1311,26 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
if (it != mapWallet.end()) { if (it != mapWallet.end()) {
CWalletTx& prevtx = it->second; CWalletTx& prevtx = it->second;
if (prevtx.isConflicted()) { if (prevtx.isConflicted()) {
MarkConflicted(prevtx.m_confirm.hashBlock, wtx.GetHash()); MarkConflicted(prevtx.m_confirm.hashBlock, prevtx.m_confirm.block_height, wtx.GetHash());
} }
} }
} }
} }
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate) bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
{ {
const CTransaction& tx = *ptx; const CTransaction& tx = *ptx;
{ {
AssertLockHeld(cs_main); // because of AddToWallet AssertLockHeld(cs_main); // because of AddToWallet
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
if (!block_hash.IsNull()) { if (!confirm.hashBlock.IsNull()) {
for (const CTxIn& txin : tx.vin) { for (const CTxIn& txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) { while (range.first != range.second) {
if (range.first->second != tx.GetHash()) { if (range.first->second != tx.GetHash()) {
WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), confirm.hashBlock.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
MarkConflicted(block_hash, range.first->second); MarkConflicted(confirm.hashBlock, confirm.block_height, range.first->second);
} }
range.first++; range.first++;
} }
@ -1351,9 +1363,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::St
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
} }
} }
if (!block_hash.IsNull()) { if (!confirm.hashBlock.IsNull()) {
int64_t block_time; int64_t block_time;
bool found_block = chain().findBlock(block_hash, nullptr /* block */, &block_time); bool found_block = chain().findBlock(confirm.hashBlock, nullptr /* block */, &block_time);
assert(found_block); assert(found_block);
if (mapKeyMetadata[keyid].nCreateTime > block_time) { if (mapKeyMetadata[keyid].nCreateTime > block_time) {
WalletLogPrintf("%s: Found a key which appears to be used earlier than we expected, updating metadata\n", __func__); WalletLogPrintf("%s: Found a key which appears to be used earlier than we expected, updating metadata\n", __func__);
@ -1372,7 +1384,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::St
// Block disconnection override an abandoned tx as unconfirmed // Block disconnection override an abandoned tx as unconfirmed
// which means user may have to call abandontransaction again // which means user may have to call abandontransaction again
wtx.SetConf(status, block_hash, posInBlock); wtx.m_confirm = confirm;
return AddToWallet(wtx, false); return AddToWallet(wtx, false);
} }
@ -1385,7 +1397,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
auto locked_chain = chain().lock(); auto locked_chain = chain().lock();
LOCK(cs_wallet); LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx); const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain(*locked_chain) == 0 && !wtx->InMempool(); return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
} }
void CWallet::MarkInputsDirty(const CTransactionRef& tx) void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@ -1398,9 +1410,9 @@ void CWallet::MarkInputsDirty(const CTransactionRef& tx)
} }
} }
bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx) bool CWallet::AbandonTransaction(const uint256& hashTx)
{ {
auto locked_chain_recursive = chain().lock(); // Temporary. Removed in upcoming lock cleanup auto locked_chain = chain().lock(); // Temporary. Removed in upcoming lock cleanup
LOCK(cs_wallet); LOCK(cs_wallet);
WalletBatch batch(*database, "r+"); WalletBatch batch(*database, "r+");
@ -1412,7 +1424,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
auto it = mapWallet.find(hashTx); auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& origtx = it->second; CWalletTx& origtx = it->second;
if (origtx.GetDepthInMainChain(locked_chain) != 0 || origtx.InMempool() || origtx.IsLockedByInstantSend()) { if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool() || origtx.IsLockedByInstantSend()) {
return false; return false;
} }
@ -1425,14 +1437,13 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(locked_chain); int currentconfirm = wtx.GetDepthInMainChain();
// If the orig tx was not in block, none of its spends can be // If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0); assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
if (currentconfirm == 0 && !wtx.isAbandoned()) { if (currentconfirm == 0 && !wtx.isAbandoned()) {
// If the orig tx was not in block/mempool, none of its spends can be in mempool // If the orig tx was not in block/mempool, none of its spends can be in mempool
assert(!wtx.InMempool()); assert(!wtx.InMempool());
wtx.m_confirm.nIndex = 0;
wtx.setAbandoned(); wtx.setAbandoned();
wtx.MarkDirty(); wtx.MarkDirty();
batch.WriteTx(wtx); batch.WriteTx(wtx);
@ -1457,13 +1468,13 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
return true; return true;
} }
void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
{ {
auto locked_chain = chain().lock(); auto locked_chain = chain().lock();
LockAnnotation lock(::cs_main); LockAnnotation lock(::cs_main);
LOCK(cs_wallet); // check WalletBatch::LoadWallet() LOCK(cs_wallet); // check WalletBatch::LoadWallet()
int conflictconfirms = -locked_chain->getBlockDepth(hashBlock); int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;
// If number of conflict confirms cannot be determined, this means // If number of conflict confirms cannot be determined, this means
// that the block is still unknown or not yet part of the main chain, // that the block is still unknown or not yet part of the main chain,
// for example when loading the wallet during a reindex. Do nothing in that // for example when loading the wallet during a reindex. Do nothing in that
@ -1486,12 +1497,13 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(*locked_chain); int currentconfirm = wtx.GetDepthInMainChain();
if (conflictconfirms < currentconfirm) { if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update. // Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block. // Mark transaction as conflicted with this block.
wtx.m_confirm.nIndex = 0; wtx.m_confirm.nIndex = 0;
wtx.m_confirm.hashBlock = hashBlock; wtx.m_confirm.hashBlock = hashBlock;
wtx.m_confirm.block_height = conflicting_height;
wtx.setConflicted(); wtx.setConflicted();
wtx.MarkDirty(); wtx.MarkDirty();
batch.WriteTx(wtx); batch.WriteTx(wtx);
@ -1513,9 +1525,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
fAnonymizableTallyCachedNonDenom = false; fAnonymizableTallyCachedNonDenom = false;
} }
void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool update_tx) void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx)
{ {
if (!AddToWalletIfInvolvingMe(ptx, status, block_hash, posInBlock, update_tx)) if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx))
return; // Not one of ours return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance // If a transaction changes 'conflicted' state, that changes the balance
@ -1530,7 +1542,8 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Status stat
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx, int64_t nAcceptTime) { void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx, int64_t nAcceptTime) {
auto locked_chain = chain().lock(); auto locked_chain = chain().lock();
LOCK(cs_wallet); LOCK(cs_wallet);
SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */); CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
SyncTransaction(ptx, confirm);
auto it = mapWallet.find(ptx->GetHash()); auto it = mapWallet.find(ptx->GetHash());
if (it != mapWallet.end()) { if (it != mapWallet.end()) {
@ -1548,28 +1561,30 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx, MemPoolR
} }
} }
void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) { void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted, int height)
{
const uint256& block_hash = block.GetHash(); const uint256& block_hash = block.GetHash();
auto locked_chain = chain().lock(); auto locked_chain = chain().lock();
LOCK(cs_wallet); LOCK(cs_wallet);
for (size_t i = 0; i < block.vtx.size(); i++) { m_last_block_processed_height = height;
SyncTransaction(block.vtx[i], CWalletTx::Status::CONFIRMED, block_hash, i); m_last_block_processed = block_hash;
// MANUAL because it's a manual removal, not using mempool logic for (size_t index = 0; index < block.vtx.size(); index++) {
TransactionRemovedFromMempool(block.vtx[i], MemPoolRemovalReason::MANUAL); CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index);
SyncTransaction(block.vtx[index], confirm);
TransactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::MANUAL);
} }
for (const CTransactionRef& ptx : vtxConflicted) { for (const CTransactionRef& ptx : vtxConflicted) {
TransactionRemovedFromMempool(ptx, MemPoolRemovalReason::MANUAL); TransactionRemovedFromMempool(ptx, MemPoolRemovalReason::MANUAL);
} }
m_last_block_processed = block_hash;
// reset cache to make sure no longer immature coins are included // reset cache to make sure no longer immature coins are included
fAnonymizableTallyCached = false; fAnonymizableTallyCached = false;
fAnonymizableTallyCachedNonDenom = false; fAnonymizableTallyCachedNonDenom = false;
} }
void CWallet::BlockDisconnected(const CBlock& block) { void CWallet::BlockDisconnected(const CBlock& block, int height)
{
auto locked_chain = chain().lock(); auto locked_chain = chain().lock();
LOCK(cs_wallet); LOCK(cs_wallet);
@ -1577,9 +1592,11 @@ void CWallet::BlockDisconnected(const CBlock& block) {
// be unconfirmed, whether or not the transaction is added back to the mempool. // be unconfirmed, whether or not the transaction is added back to the mempool.
// User may have to call abandontransaction again. It may be addressed in the // User may have to call abandontransaction again. It may be addressed in the
// future with a stickier abandoned state or even removing abandontransaction call. // future with a stickier abandoned state or even removing abandontransaction call.
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) { for (const CTransactionRef& ptx : block.vtx) {
// NOTE: do NOT pass pindex here CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */); SyncTransaction(ptx, confirm);
} }
// reset cache to make sure no longer mature coins are excluded // reset cache to make sure no longer mature coins are excluded
@ -1600,7 +1617,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
// for the queue to drain enough to execute it (indicating we are caught up // for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function). // at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed); uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
chain().waitForNotificationsIfNewBlocksConnected(last_block_hash); chain().waitForNotificationsIfTipChanged(last_block_hash);
} }
@ -2430,7 +2447,8 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break; break;
} }
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
SyncTransaction(block.vtx[posInBlock], CWalletTx::Status::CONFIRMED, block_hash, posInBlock, fUpdate); CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *block_height, block_hash, posInBlock);
SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
} }
// scan succeeded, record block as most recent successfully scanned // scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash; result.last_scanned_block = block_hash;
@ -2478,7 +2496,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
return result; return result;
} }
void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) void CWallet::ReacceptWalletTransactions()
{ {
// If transactions aren't being broadcasted, don't let them into local mempool either // If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions) if (!fBroadcastTransactions)
@ -2491,7 +2509,7 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid); assert(wtx.GetHash() == wtxid);
int nDepth = wtx.GetDepthInMainChain(locked_chain); int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.IsLockedByInstantSend() && !wtx.isAbandoned())) { if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.IsLockedByInstantSend() && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@ -2502,11 +2520,11 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) { for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second); CWalletTx& wtx = *(item.second);
std::string unused_err_string; std::string unused_err_string;
wtx.SubmitMemoryPoolAndRelay(unused_err_string, false, locked_chain); wtx.SubmitMemoryPoolAndRelay(unused_err_string, false);
} }
} }
bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain) bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
{ {
// Can't relay if wallet is not broadcasting // Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false; if (!pwallet->GetBroadcastTransactions()) return false;
@ -2516,7 +2534,7 @@ bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, in
// cause log spam. // cause log spam.
if (IsCoinBase()) return false; if (IsCoinBase()) return false;
// Don't try to submit conflicted or confirmed transactions. // Don't try to submit conflicted or confirmed transactions.
if (GetDepthInMainChain(locked_chain) != 0) return false; if (GetDepthInMainChain() != 0) return false;
// Don't try to submit transactions locked via InstantSend. // Don't try to submit transactions locked via InstantSend.
if (IsLockedByInstantSend()) return false; if (IsLockedByInstantSend()) return false;
@ -2573,10 +2591,10 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
return debit; return debit;
} }
CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const CAmount CWalletTx::GetCredit(const isminefilter& filter) const
{ {
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain)) if (IsImmatureCoinBase())
return 0; return 0;
CAmount credit = 0; CAmount credit = 0;
@ -2590,16 +2608,16 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return credit; return credit;
} }
CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
{ {
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) { if (IsImmatureCoinBase() && IsInMainChain()) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache); return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
} }
return 0; return 0;
} }
CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache, const isminefilter& filter) const CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{ {
if (pwallet == nullptr) if (pwallet == nullptr)
return 0; return 0;
@ -2608,7 +2626,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY; bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain)) if (IsImmatureCoinBase())
return 0; return 0;
if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
@ -2619,7 +2637,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
uint256 hashTx = GetHash(); uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++) for (unsigned int i = 0; i < tx->vout.size(); i++)
{ {
if (!pwallet->IsSpent(locked_chain, hashTx, i)) if (!pwallet->IsSpent(hashTx, i))
{ {
const CTxOut &txout = tx->vout[i]; const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter); nCredit += pwallet->GetCredit(txout, filter);
@ -2635,16 +2653,16 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
return nCredit; return nCredit;
} }
CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
{ {
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) { if (IsImmatureCoinBase() && IsInMainChain()) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache); return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
} }
return 0; return 0;
} }
CAmount CWalletTx::GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, const CCoinControl* coinControl) const CAmount CWalletTx::GetAnonymizedCredit(const CCoinControl* coinControl) const
{ {
if (!pwallet) if (!pwallet)
return 0; return 0;
@ -2652,7 +2670,7 @@ CAmount CWalletTx::GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, co
AssertLockHeld(pwallet->cs_wallet); AssertLockHeld(pwallet->cs_wallet);
// Exclude coinbase and conflicted txes // Exclude coinbase and conflicted txes
if (IsCoinBase() || GetDepthInMainChain(locked_chain) < 0) if (IsCoinBase() || GetDepthInMainChain() < 0)
return 0; return 0;
if (coinControl == nullptr && m_amounts[ANON_CREDIT].m_cached[ISMINE_SPENDABLE]) if (coinControl == nullptr && m_amounts[ANON_CREDIT].m_cached[ISMINE_SPENDABLE])
@ -2669,7 +2687,7 @@ CAmount CWalletTx::GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, co
continue; continue;
} }
if (pwallet->IsSpent(locked_chain, hashTx, i) || !CCoinJoin::IsDenominatedAmount(txout.nValue)) continue; if (pwallet->IsSpent(hashTx, i) || !CCoinJoin::IsDenominatedAmount(txout.nValue)) continue;
if (pwallet->IsFullyMixed(outpoint)) { if (pwallet->IsFullyMixed(outpoint)) {
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
@ -2693,10 +2711,10 @@ CAmount CWalletTx::GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, b
AssertLockHeld(pwallet->cs_wallet); AssertLockHeld(pwallet->cs_wallet);
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity(locked_chain) > 0) if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0; return 0;
int nDepth = GetDepthInMainChain(locked_chain); int nDepth = GetDepthInMainChain();
if (nDepth < 0) return 0; if (nDepth < 0) return 0;
bool isUnconfirmed = IsTrusted(locked_chain) && nDepth == 0; bool isUnconfirmed = IsTrusted(locked_chain) && nDepth == 0;
@ -2716,7 +2734,7 @@ CAmount CWalletTx::GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, b
{ {
const CTxOut &txout = tx->vout[i]; const CTxOut &txout = tx->vout[i];
if (pwallet->IsSpent(locked_chain, hashTx, i) || !CCoinJoin::IsDenominatedAmount(txout.nValue)) continue; if (pwallet->IsSpent(hashTx, i) || !CCoinJoin::IsDenominatedAmount(txout.nValue)) continue;
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit)) if (!MoneyRange(nCredit))
@ -2750,7 +2768,7 @@ bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
// Quick answer in most cases // Quick answer in most cases
if (!locked_chain.checkFinalTx(*tx)) if (!locked_chain.checkFinalTx(*tx))
return false; return false;
int nDepth = GetDepthInMainChain(locked_chain); int nDepth = GetDepthInMainChain();
if (nDepth >= 1) if (nDepth >= 1)
return true; return true;
if (nDepth < 0) if (nDepth < 0)
@ -2827,7 +2845,7 @@ void CWallet::ResendWalletTransactions()
// any confirmed or conflicting txs. // any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue; if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
std::string unused_err_string; std::string unused_err_string;
if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true, *locked_chain)) ++submitted_tx_count; if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
} }
} // locked_chain and cs_wallet } // locked_chain and cs_wallet
@ -2881,9 +2899,9 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, const bool fAddLocked,
LOCK(cs_wallet); LOCK(cs_wallet);
for (auto pcoin : GetSpendableTXs()) { for (auto pcoin : GetSpendableTXs()) {
const bool is_trusted{pcoin->IsTrusted(*locked_chain)}; const bool is_trusted{pcoin->IsTrusted(*locked_chain)};
const int tx_depth{pcoin->GetDepthInMainChain(*locked_chain)}; const int tx_depth{pcoin->GetDepthInMainChain()};
const CAmount tx_credit_mine{pcoin->GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)}; const CAmount tx_credit_mine{pcoin->GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE)};
const CAmount tx_credit_watchonly{pcoin->GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)}; const CAmount tx_credit_watchonly{pcoin->GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY)};
if (is_trusted && ((tx_depth >= min_depth) || (fAddLocked && pcoin->IsLockedByInstantSend()))) { if (is_trusted && ((tx_depth >= min_depth) || (fAddLocked && pcoin->IsLockedByInstantSend()))) {
ret.m_mine_trusted += tx_credit_mine; ret.m_mine_trusted += tx_credit_mine;
ret.m_watchonly_trusted += tx_credit_watchonly; ret.m_watchonly_trusted += tx_credit_watchonly;
@ -2892,10 +2910,10 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, const bool fAddLocked,
ret.m_mine_untrusted_pending += tx_credit_mine; ret.m_mine_untrusted_pending += tx_credit_mine;
ret.m_watchonly_untrusted_pending += tx_credit_watchonly; ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
} }
ret.m_mine_immature += pcoin->GetImmatureCredit(*locked_chain); ret.m_mine_immature += pcoin->GetImmatureCredit();
ret.m_watchonly_immature += pcoin->GetImmatureWatchOnlyCredit(*locked_chain); ret.m_watchonly_immature += pcoin->GetImmatureWatchOnlyCredit();
if (CCoinJoinClientOptions::IsEnabled()) { if (CCoinJoinClientOptions::IsEnabled()) {
ret.m_anonymized += pcoin->GetAnonymizedCredit(*locked_chain, coinControl); ret.m_anonymized += pcoin->GetAnonymizedCredit(coinControl);
ret.m_denominated_trusted += pcoin->GetDenominatedCredit(*locked_chain, false); ret.m_denominated_trusted += pcoin->GetDenominatedCredit(*locked_chain, false);
ret.m_denominated_untrusted_pending += pcoin->GetDenominatedCredit(*locked_chain, true); ret.m_denominated_untrusted_pending += pcoin->GetDenominatedCredit(*locked_chain, true);
} }
@ -2964,7 +2982,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const
CAmount nValue = it->second.tx->vout[outpoint.n].nValue; CAmount nValue = it->second.tx->vout[outpoint.n].nValue;
if (!CCoinJoin::IsDenominatedAmount(nValue)) continue; if (!CCoinJoin::IsDenominatedAmount(nValue)) continue;
if (it->second.GetDepthInMainChain(*locked_chain) < 0) continue; if (it->second.GetDepthInMainChain() < 0) continue;
int nRounds = GetCappedOutpointCoinJoinRounds(outpoint); int nRounds = GetCappedOutpointCoinJoinRounds(outpoint);
nTotal += nValue * nRounds / CCoinJoinClientOptions::GetRounds(); nTotal += nValue * nRounds / CCoinJoinClientOptions::GetRounds();
@ -3004,10 +3022,10 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (!locked_chain.checkFinalTx(*pcoin->tx)) if (!locked_chain.checkFinalTx(*pcoin->tx))
continue; continue;
if (pcoin->IsImmatureCoinBase(locked_chain)) if (pcoin->IsImmatureCoinBase())
continue; continue;
int nDepth = pcoin->GetDepthInMainChain(locked_chain); int nDepth = pcoin->GetDepthInMainChain();
// We should not consider coins which aren't at least in our mempool // We should not consider coins which aren't at least in our mempool
// It's possible for these to be conflicted via ancestors which we may never be able to detect // It's possible for these to be conflicted via ancestors which we may never be able to detect
@ -3052,7 +3070,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (IsLockedCoin(wtxid, i) && nCoinType != CoinType::ONLY_MASTERNODE_COLLATERAL) if (IsLockedCoin(wtxid, i) && nCoinType != CoinType::ONLY_MASTERNODE_COLLATERAL)
continue; continue;
if (IsSpent(locked_chain, wtxid, i)) if (IsSpent(wtxid, i))
continue; continue;
isminetype mine = IsMine(pcoin->tx->vout[i]); isminetype mine = IsMine(pcoin->tx->vout[i]);
@ -3105,7 +3123,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
for (const COutPoint& output : lockedCoins) { for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash); auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) { if (it != mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(locked_chain); int depth = it->second.GetDepthInMainChain();
if (depth >= 0 && output.n < it->second.tx->vout.size() && if (depth >= 0 && output.n < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) { IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
CTxDestination address; CTxDestination address;
@ -3515,9 +3533,9 @@ std::vector<CompactTallyItem> CWallet::SelectCoinsGroupedByAddresses(bool fSkipD
const CWalletTx& wtx = (*it).second; const CWalletTx& wtx = (*it).second;
if(wtx.IsCoinBase() && wtx.GetBlocksToMaturity(*locked_chain) > 0) continue; if(wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0) continue;
if(fSkipUnconfirmed && !wtx.IsTrusted(*locked_chain)) continue; if(fSkipUnconfirmed && !wtx.IsTrusted(*locked_chain)) continue;
if (wtx.GetDepthInMainChain(*locked_chain) < 0) continue; if (wtx.GetDepthInMainChain() < 0) continue;
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
CTxDestination txdest; CTxDestination txdest;
@ -3529,7 +3547,7 @@ std::vector<CompactTallyItem> CWallet::SelectCoinsGroupedByAddresses(bool fSkipD
auto itTallyItem = mapTally.find(txdest); auto itTallyItem = mapTally.find(txdest);
if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() && int64_t(itTallyItem->second.vecInputCoins.size()) >= nMaxOupointsPerAddress) continue; if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() && int64_t(itTallyItem->second.vecInputCoins.size()) >= nMaxOupointsPerAddress) continue;
if(IsSpent(*locked_chain, outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue; if(IsSpent(outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue;
if(fSkipDenominated && CCoinJoin::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue; if(fSkipDenominated && CCoinJoin::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue;
@ -3621,7 +3639,7 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) const
const auto it = mapWallet.find(outpoint.hash); const auto it = mapWallet.find(outpoint.hash);
if (it == mapWallet.end()) continue; if (it == mapWallet.end()) continue;
if (it->second.tx->vout[outpoint.n].nValue != nInputAmount) continue; if (it->second.tx->vout[outpoint.n].nValue != nInputAmount) continue;
if (it->second.GetDepthInMainChain(*locked_chain) < 0) continue; if (it->second.GetDepthInMainChain() < 0) continue;
nTotal++; nTotal++;
} }
@ -4088,7 +4106,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
} }
std::string err_string; std::string err_string;
if (!wtx.SubmitMemoryPoolAndRelay(err_string, true, *locked_chain)) { if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string); WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} }
@ -4125,10 +4143,16 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
fFirstRunRet = mapKeys.empty() && mapHdPubKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET); fFirstRunRet = mapKeys.empty() && mapHdPubKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
} }
for (auto& pair : mapWallet) { if (locked_chain) {
for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) { const Optional<int> tip_height = locked_chain->getHeight();
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(*chain().lock(), pair.first, i)) { if (tip_height) {
setWalletUTXO.insert(COutPoint(pair.first, i)); SetLastBlockProcessed(*tip_height, locked_chain->getBlockHash(*tip_height));
for (auto& pair : mapWallet) {
for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {
setWalletUTXO.insert(COutPoint(pair.first, i));
}
}
} }
} }
} }
@ -4151,7 +4175,7 @@ void CWallet::AutoLockMasternodeCollaterals()
LOCK(cs_wallet); LOCK(cs_wallet);
for (const auto& pair : mapWallet) { for (const auto& pair : mapWallet) {
for (unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) { for (unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
if (IsMine(pair.second.tx->vout[i]) && !IsSpent(*locked_chain, pair.first, i)) { if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {
if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) { if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) {
LockCoin(COutPoint(pair.first, i)); LockCoin(COutPoint(pair.first, i));
} }
@ -4563,10 +4587,10 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
if (!pcoin->IsTrusted(locked_chain)) if (!pcoin->IsTrusted(locked_chain))
continue; continue;
if (pcoin->IsImmatureCoinBase(locked_chain)) if (pcoin->IsImmatureCoinBase())
continue; continue;
int nDepth = pcoin->GetDepthInMainChain(locked_chain); int nDepth = pcoin->GetDepthInMainChain();
if ((nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) && !pcoin->IsLockedByInstantSend()) if ((nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) && !pcoin->IsLockedByInstantSend())
continue; continue;
@ -4578,7 +4602,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
continue; continue;
CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
if (!balances.count(addr)) if (!balances.count(addr))
balances[addr] = 0; balances[addr] = 0;
@ -5310,8 +5334,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
const Optional<int> tip_height = locked_chain->getHeight(); const Optional<int> tip_height = locked_chain->getHeight();
if (tip_height) { if (tip_height) {
walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height); walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height);
walletInstance->m_last_block_processed_height = *tip_height;
} else { } else {
walletInstance->m_last_block_processed.SetNull(); walletInstance->m_last_block_processed.SetNull();
walletInstance->m_last_block_processed_height = -1;
} }
if (tip_height && *tip_height != rescan_height) if (tip_height && *tip_height != rescan_height)
@ -5411,7 +5437,7 @@ void CWallet::postInitProcess()
// Add wallet transactions that aren't already in a block to mempool // Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded // Do this here as mempool requires genesis block to be loaded
ReacceptWalletTransactions(*locked_chain); ReacceptWalletTransactions();
// Update wallet transactions with current mempool transactions. // Update wallet transactions with current mempool transactions.
chain().requestMempoolTransactions(*this); chain().requestMempoolTransactions(*this);
@ -5630,23 +5656,13 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool fInternalIn)
fInternal = fInternalIn; fInternal = fInternalIn;
} }
void CWalletTx::SetConf(Status status, const uint256& block_hash, int posInBlock) int CWalletTx::GetDepthInMainChain() const
{
// Update tx status
m_confirm.status = status;
// Update the tx's hashBlock
m_confirm.hashBlock = block_hash;
// set the position of the transaction in the block
m_confirm.nIndex = posInBlock;
}
int CWalletTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
{ {
assert(pwallet != nullptr);
AssertLockHeld(pwallet->cs_wallet);
if (isUnconfirmed() || isAbandoned()) return 0; if (isUnconfirmed() || isAbandoned()) return 0;
return locked_chain.getBlockDepth(m_confirm.hashBlock) * (isConflicted() ? -1 : 1); return (pwallet->GetLastBlockHeight() - m_confirm.block_height + 1) * (isConflicted() ? -1 : 1);
} }
bool CWalletTx::IsLockedByInstantSend() const bool CWalletTx::IsLockedByInstantSend() const
@ -5671,19 +5687,19 @@ bool CWalletTx::IsChainLocked() const
return fIsChainlocked; return fIsChainlocked;
} }
int CWalletTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const int CWalletTx::GetBlocksToMaturity() const
{ {
if (!IsCoinBase()) if (!IsCoinBase())
return 0; return 0;
int chain_depth = GetDepthInMainChain(locked_chain); int chain_depth = GetDepthInMainChain();
assert(chain_depth >= 0); // coinbase tx should not be conflicted assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth); return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
} }
bool CWalletTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const bool CWalletTx::IsImmatureCoinBase() const
{ {
// note GetBlocksToMaturity is 0 for non-coinbase tx // note GetBlocksToMaturity is 0 for non-coinbase tx
return GetBlocksToMaturity(locked_chain) > 0; return GetBlocksToMaturity() > 0;
} }
std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const { std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const {

View File

@ -374,14 +374,17 @@ public:
ABANDONED ABANDONED
}; };
/* Confirmation includes tx status and a pair of {block hash/tx index in block} at which tx has been confirmed. /* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
* This pair is both 0 if tx hasn't confirmed yet. Meaning of these fields changes with CONFLICTED state * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
* where they instead point to block hash and index of the deepest conflicting tx. * Meaning of these fields changes with CONFLICTED state where they instead point to block hash
* and block height of the deepest conflicting tx.
*/ */
struct Confirmation { struct Confirmation {
Status status = UNCONFIRMED; Status status;
uint256 hashBlock = uint256(); int block_height;
int nIndex = 0; uint256 hashBlock;
int nIndex;
Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
}; };
Confirmation m_confirm; Confirmation m_confirm;
@ -424,7 +427,6 @@ public:
* compatibility (pre-commit 9ac63d6). * compatibility (pre-commit 9ac63d6).
*/ */
if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) { if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
m_confirm.hashBlock = uint256();
setAbandoned(); setAbandoned();
} else if (serializedIndex == -1) { } else if (serializedIndex == -1) {
setConflicted(); setConflicted();
@ -473,17 +475,17 @@ public:
//! filter decides which addresses will count towards the debit //! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const; CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const; CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const; CAmount GetImmatureCredit(bool fUseCache = true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet. // having to resolve the issue of member access into incomplete type CWallet.
CAmount GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS; CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
CAmount GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache=true) const; CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
CAmount GetChange() const; CAmount GetChange() const;
CAmount GetAnonymizedCredit(interfaces::Chain::Lock& locked_chain, const CCoinControl* coinControl = nullptr) const; CAmount GetAnonymizedCredit(const CCoinControl* coinControl = nullptr) const;
CAmount GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, bool unconfirmed, bool fUseCache=true) const; CAmount GetDenominatedCredit(interfaces::Chain::Lock& locked_chain, bool unconfirmed, bool fUseCache=true) const;
// Get the marginal bytes if spending the specified output from this transaction // Get the marginal bytes if spending the specified output from this transaction
@ -509,7 +511,7 @@ public:
int64_t GetTxTime() const; int64_t GetTxTime() const;
// Pass this transaction to node for mempool insertion and relay to peers if flag set to true // Pass this transaction to node for mempool insertion and relay to peers if flag set to true
bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain); bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@ -519,16 +521,20 @@ public:
// in place. // in place.
std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS; std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
void SetConf(Status status, const uint256& block_hash, int posInBlock);
/** /**
* Return depth of transaction in blockchain: * Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain * <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block * 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain * >=1 : this many blocks deep in the main chain
*/ */
int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const; // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; } // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
// "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
// resolve the issue of member access into incomplete type CWallet. Note
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
bool IsLockedByInstantSend() const; bool IsLockedByInstantSend() const;
bool IsChainLocked() const; bool IsChainLocked() const;
@ -537,22 +543,24 @@ public:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction * 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks * >0 : is a coinbase transaction which matures in this many blocks
*/ */
int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const; int GetBlocksToMaturity() const;
bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
void setAbandoned() void setAbandoned()
{ {
m_confirm.status = CWalletTx::ABANDONED; m_confirm.status = CWalletTx::ABANDONED;
m_confirm.hashBlock = uint256(); m_confirm.hashBlock = uint256();
m_confirm.block_height = 0;
m_confirm.nIndex = 0; m_confirm.nIndex = 0;
} }
bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; } bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; } void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; } bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; } void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); } const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const; bool IsImmatureCoinBase() const;
}; };
struct WalletTxHasher struct WalletTxHasher
@ -689,10 +697,10 @@ private:
* Abandoned state should probably be more carefully tracked via different * Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary. * posInBlock signals or by checking mempool presence when necessary.
*/ */
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
/* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */ /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -701,7 +709,7 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions. /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* HD derive new child key (on internal or external chain) */ /* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secretRet, uint32_t nAccountIndex, bool fInternal /*= false*/) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secretRet, uint32_t nAccountIndex, bool fInternal /*= false*/) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -741,9 +749,8 @@ private:
* The following is used to keep track of how far behind the wallet is * The following is used to keep track of how far behind the wallet is
* from the chain sync, and to allow clients to block on us being caught up. * from the chain sync, and to allow clients to block on us being caught up.
* *
* Note that this is *not* how far we've processed, we may need some rescan * Processed hash is a pointer on node's tip and doesn't imply that the wallet
* to have seen all transactions in the chain, but is only used to track * has scanned sequentially all blocks up to this one.
* live BlockConnected callbacks.
*/ */
uint256 m_last_block_processed GUARDED_BY(cs_wallet); uint256 m_last_block_processed GUARDED_BY(cs_wallet);
@ -758,6 +765,13 @@ private:
*/ */
void InitCoinJoinSalt(); void InitCoinJoinSalt();
/* Height of last block processed is used by wallet to know depth of transactions
* without relying on Chain interface beyond asynchronous updates. For safety, we
* initialize it to -1. Height is a pointer on node's tip and doesn't imply
* that the wallet has scanned sequentially all blocks up to this one.
*/
int m_last_block_processed_height GUARDED_BY(cs_wallet) = -1;
public: public:
/* /*
* Main wallet lock. * Main wallet lock.
@ -893,7 +907,7 @@ public:
bool IsDenominated(const COutPoint& outpoint) const; bool IsDenominated(const COutPoint& outpoint) const;
bool IsFullyMixed(const COutPoint& outpoint) const; bool IsFullyMixed(const COutPoint& outpoint) const;
bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const; std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -998,8 +1012,8 @@ public:
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) override; void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) override;
void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override; void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted, int height) override;
void BlockDisconnected(const CBlock& block) override; void BlockDisconnected(const CBlock& block, int height) override;
void UpdatedBlockTip() override; void UpdatedBlockTip() override;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
@ -1020,7 +1034,7 @@ public:
}; };
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate); ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx, MemPoolRemovalReason reason) override; void TransactionRemovedFromMempool(const CTransactionRef &ptx, MemPoolRemovalReason reason) override;
void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions(); void ResendWalletTransactions();
struct Balance { struct Balance {
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
@ -1234,7 +1248,7 @@ public:
bool TransactionCanBeAbandoned(const uint256& hashTx) const; bool TransactionCanBeAbandoned(const uint256& hashTx) const;
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx); bool AbandonTransaction(const uint256& hashTx);
//! Verify wallet naming and perform salvage on the wallet if required //! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings); static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
@ -1332,6 +1346,21 @@ public:
/** Add a KeyOriginInfo to the wallet */ /** Add a KeyOriginInfo to the wallet */
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info); bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
/** Get last block processed height */
int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
assert(m_last_block_processed_height >= 0);
return m_last_block_processed_height;
};
/** Set last block processed height, currently only use in unit test */
void SetLastBlockProcessed(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
m_last_block_processed_height = block_height;
m_last_block_processed = block_hash;
};
}; };
/** /**

View File

@ -509,7 +509,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
bool fNoncriticalErrors = false; bool fNoncriticalErrors = false;
DBErrors result = DBErrors::LOAD_OK; DBErrors result = DBErrors::LOAD_OK;
auto locked_chain = pwallet->chain().lock(); auto locked_chain = pwallet->LockChain();
LockAnnotation lock(::cs_main); LockAnnotation lock(::cs_main);
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
try { try {