Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FreshEyes] locks: introduce mutex for tx download, flush rejection filters on UpdatedBlockTip #17

Open
wants to merge 7 commits into
base: bitcoin-fresheyes-staging-master-30111
Choose a base branch
from
130 changes: 66 additions & 64 deletions src/net_processing.cpp

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions src/test/orphanage_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
class TxOrphanageTest : public TxOrphanage
{
public:
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
inline size_t CountOrphans() const
{
LOCK(m_mutex);
return m_orphans.size();
}

CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
CTransactionRef RandomOrphan()
{
LOCK(m_mutex);
std::map<Wtxid, OrphanTx>::iterator it;
it = m_orphans.lower_bound(Wtxid::FromUint256(InsecureRand256()));
if (it == m_orphans.end())
Expand Down
34 changes: 6 additions & 28 deletions src/txorphanage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;

bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
{
LOCK(m_mutex);

const Txid& hash = tx->GetHash();
const Wtxid& wtxid = tx->GetWitnessHash();
if (m_orphans.count(wtxid))
Expand Down Expand Up @@ -54,13 +52,11 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)

int TxOrphanage::EraseTx(const Wtxid& wtxid)
{
LOCK(m_mutex);
return EraseTxNoLock(wtxid);
return EraseTxInternal(wtxid);
}

int TxOrphanage::EraseTxNoLock(const Wtxid& wtxid)
int TxOrphanage::EraseTxInternal(const Wtxid& wtxid)
{
AssertLockHeld(m_mutex);
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
if (it == m_orphans.end())
return 0;
Expand Down Expand Up @@ -96,8 +92,6 @@ int TxOrphanage::EraseTxNoLock(const Wtxid& wtxid)

void TxOrphanage::EraseForPeer(NodeId peer)
{
LOCK(m_mutex);

m_peer_work_set.erase(peer);

int nErased = 0;
Expand All @@ -107,16 +101,14 @@ void TxOrphanage::EraseForPeer(NodeId peer)
// increment to avoid iterator becoming invalid after erasure
const auto& [wtxid, orphan] = *iter++;
if (orphan.fromPeer == peer) {
nErased += EraseTxNoLock(wtxid);
nErased += EraseTxInternal(wtxid);
}
}
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
}

void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
{
LOCK(m_mutex);

unsigned int nEvicted = 0;
static int64_t nNextSweep;
int64_t nNow = GetTime();
Expand All @@ -129,7 +121,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
{
std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
if (maybeErase->second.nTimeExpire <= nNow) {
nErased += EraseTxNoLock(maybeErase->second.tx->GetWitnessHash());
nErased += EraseTxInternal(maybeErase->second.tx->GetWitnessHash());
} else {
nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
}
Expand All @@ -142,17 +134,14 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
{
// Evict a random orphan:
size_t randompos = rng.randrange(m_orphan_list.size());
EraseTxNoLock(m_orphan_list[randompos]->second.tx->GetWitnessHash());
EraseTxInternal(m_orphan_list[randompos]->second.tx->GetWitnessHash());
++nEvicted;
}
if (nEvicted > 0) LogPrint(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
}

void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
{
LOCK(m_mutex);


for (unsigned int i = 0; i < tx.vout.size(); i++) {
const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
if (it_by_prev != m_outpoint_to_orphan_it.end()) {
Expand All @@ -171,14 +160,11 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)

bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
{
LOCK(m_mutex);
return m_orphans.count(wtxid);
}

CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
{
LOCK(m_mutex);

auto work_set_it = m_peer_work_set.find(peer);
if (work_set_it != m_peer_work_set.end()) {
auto& work_set = work_set_it->second;
Expand All @@ -197,8 +183,6 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)

bool TxOrphanage::HaveTxToReconsider(NodeId peer)
{
LOCK(m_mutex);

auto work_set_it = m_peer_work_set.find(peer);
if (work_set_it != m_peer_work_set.end()) {
auto& work_set = work_set_it->second;
Expand All @@ -209,8 +193,6 @@ bool TxOrphanage::HaveTxToReconsider(NodeId peer)

void TxOrphanage::EraseForBlock(const CBlock& block)
{
LOCK(m_mutex);

std::vector<Wtxid> vOrphanErase;

for (const CTransactionRef& ptx : block.vtx) {
Expand All @@ -231,16 +213,14 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
if (vOrphanErase.size()) {
int nErased = 0;
for (const auto& orphanHash : vOrphanErase) {
nErased += EraseTxNoLock(orphanHash);
nErased += EraseTxInternal(orphanHash);
}
LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
}
}

std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
{
LOCK(m_mutex);

// First construct a vector of iterators to ensure we do not return duplicates of the same tx
// and so we can sort by nTimeExpire.
std::vector<OrphanMap::iterator> iters;
Expand Down Expand Up @@ -281,8 +261,6 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac

std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const
{
LOCK(m_mutex);

// First construct vector of iterators to ensure we do not return duplicates of the same tx.
std::vector<OrphanMap::iterator> iters;

Expand Down
38 changes: 17 additions & 21 deletions src/txorphanage.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,51 @@
class TxOrphanage {
public:
/** Add a new orphan transaction */
bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
bool AddTx(const CTransactionRef& tx, NodeId peer);

/** Check if we already have an orphan transaction (by wtxid only) */
bool HaveTx(const Wtxid& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
bool HaveTx(const Wtxid& wtxid) const;

/** Extract a transaction from a peer's work set
* Returns nullptr if there are no transactions to work on.
* Otherwise returns the transaction reference, and removes
* it from the work set.
*/
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
CTransactionRef GetTxToReconsider(NodeId peer);

/** Erase an orphan by wtxid */
int EraseTx(const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
int EraseTx(const Wtxid& wtxid);

/** Erase all orphans announced by a peer (eg, after that peer disconnects) */
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
void EraseForPeer(NodeId peer);

/** Erase all orphans included in or invalidated by a new block */
void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
void EraseForBlock(const CBlock& block);

/** Limit the orphanage to the given maximum */
void LimitOrphans(unsigned int max_orphans, FastRandomContext& rng) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
void LimitOrphans(unsigned int max_orphans, FastRandomContext& rng);

/** Add any orphans that list a particular tx as a parent into the from peer's work set */
void AddChildrenToWorkSet(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
void AddChildrenToWorkSet(const CTransaction& tx);

/** Does this peer have any work to do? */
bool HaveTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
bool HaveTxToReconsider(NodeId peer);

/** Get all children that spend from this tx and were received from nodeid. Sorted from most
* recent to least recent. */
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const;

/** Get all children that spend from this tx but were not received from nodeid. Also return
* which peer provided each tx. */
std::vector<std::pair<CTransactionRef, NodeId>> GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
std::vector<std::pair<CTransactionRef, NodeId>> GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const;

/** Return how many entries exist in the orphange */
size_t Size() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
size_t Size()
{
LOCK(m_mutex);
return m_orphans.size();
}

protected:
/** Guards orphan transactions */
mutable Mutex m_mutex;

struct OrphanTx {
CTransactionRef tx;
NodeId fromPeer;
Expand All @@ -79,10 +75,10 @@ class TxOrphanage {

/** Map from wtxid to orphan transaction record. Limited by
* -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
std::map<Wtxid, OrphanTx> m_orphans GUARDED_BY(m_mutex);
std::map<Wtxid, OrphanTx> m_orphans;

/** Which peer provided the orphans that need to be reconsidered */
std::map<NodeId, std::set<Wtxid>> m_peer_work_set GUARDED_BY(m_mutex);
std::map<NodeId, std::set<Wtxid>> m_peer_work_set;

using OrphanMap = decltype(m_orphans);

Expand All @@ -97,13 +93,13 @@ class TxOrphanage {

/** Index from the parents' COutPoint into the m_orphans. Used
* to remove orphan transactions from the m_orphans */
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(m_mutex);
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it;

/** Orphan transactions in vector for quick random eviction */
std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(m_mutex);
std::vector<OrphanMap::iterator> m_orphan_list;

/** Erase an orphan by wtxid */
int EraseTxNoLock(const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
int EraseTxInternal(const Wtxid& wtxid);
};

#endif // BITCOIN_TXORPHANAGE_H
12 changes: 11 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,8 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<

{
LOCK(cs_main);
bool notify_updated_tip = false;
{
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
LOCK(MempoolMutex());
const bool was_in_ibd = m_chainman.IsInitialBlockDownload();
Expand Down Expand Up @@ -3394,6 +3396,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
if (this == &m_chainman.ActiveChainstate() && pindexFork != pindexNewTip) {
// Notify ValidationInterface subscribers
if (m_chainman.m_options.signals) {
notify_updated_tip = true;
m_chainman.m_options.signals->UpdatedBlockTip(pindexNewTip, pindexFork, still_in_ibd);
}

Expand All @@ -3406,7 +3409,11 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
break;
}
}
}
} // release MempoolMutex
if (notify_updated_tip) {
m_chainman.m_options.signals->UpdatedBlockTipSync(pindexNewTip);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 authors commented here with:

  • comment link https://github.com/bitcoin/bitcoin/pull/30111#discussion_r1601903709 at 2024/05/15, 16:00:46 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/30111#discussion_r1601920676 at 2024/05/15, 16:13:15 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/30111#discussion_r1601923988 at 2024/05/15, 16:15:58 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/30111#discussion_r1601946313 at 2024/05/15, 16:33:58 UTC.

}
} // release cs_main
// When we reach this point, we switched to a new tip (stored in pindexNewTip).

if (exited_ibd) {
Expand Down Expand Up @@ -3625,6 +3632,9 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// distinguish user-initiated invalidateblock changes from other
// changes.
(void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_reindexing), *to_mark_failed->pprev);
if (m_chainman.m_options.signals) {
m_chainman.m_options.signals->UpdatedBlockTipSync(m_chain.Tip());
}
}
return true;
}
Expand Down
6 changes: 6 additions & 0 deletions src/validationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlo
fInitialDownload);
}

void ValidationSignals::UpdatedBlockTipSync(const CBlockIndex *pindexNew)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An author commented here with:

  • comment link https://github.com/bitcoin/bitcoin/pull/30111#discussion_r1601944049 at 2024/05/15, 16:32:08 UTC.

{
LOG_EVENT("%s: new block hash=%s block height=%d", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight);
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTipSync(pindexNew); });
}

void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
{
auto event = [tx, mempool_sequence, this] {
Expand Down
5 changes: 5 additions & 0 deletions src/validationinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class CValidationInterface {
* Called on a background thread. Only called for the active chainstate.
*/
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
/**
* Notifies listeners when the block chain tip advances synchronously.
*/
virtual void UpdatedBlockTipSync(const CBlockIndex* pindexNew) {};
/**
* Notifies listeners of a transaction having been added to mempool.
*
Expand Down Expand Up @@ -214,6 +218,7 @@ class ValidationSignals {
void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main);

void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
void UpdatedBlockTipSync(const CBlockIndex*);
void TransactionAddedToMempool(const NewMempoolTransactionInfo&, uint64_t mempool_sequence);
void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence);
void MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>&, unsigned int nBlockHeight);
Expand Down
Loading