Skip to content

Commit 9645775

Browse files
committed
Split up and optimize transaction and block inv queues
An adaptation of btc@dc13dcd2bec2613a1cd5e0395b09b449d176146f with a tier two INV vector.
1 parent 68bc68f commit 9645775

File tree

2 files changed

+75
-38
lines changed

2 files changed

+75
-38
lines changed

src/net.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,15 @@ class CNode
610610

611611
// inventory based relay
612612
CRollingBloomFilter filterInventoryKnown;
613-
std::vector<CInv> vInventoryToSend;
613+
// Set of transaction ids we still have to announce.
614+
// They are sorted by the mempool before relay, so the order is not important.
615+
std::set<uint256> setInventoryTxToSend;
616+
// List of block ids we still have announce.
617+
// There is no final sorting before sending, as they are always sent immediately
618+
// and in the order requested.
619+
std::vector<uint256> vInventoryBlockToSend;
620+
// Set of tier two messages ids we still have to announce.
621+
std::vector<CInv> vInventoryTierTwoToSend;
614622
RecursiveMutex cs_inventory;
615623
std::multimap<int64_t, CInv> mapAskFor;
616624
std::vector<uint256> vBlockRequested;
@@ -735,11 +743,15 @@ class CNode
735743

736744
void PushInventory(const CInv& inv)
737745
{
738-
{
739-
LOCK(cs_inventory);
740-
if (inv.type == MSG_TX && filterInventoryKnown.contains(inv.hash))
741-
return;
742-
vInventoryToSend.push_back(inv);
746+
LOCK(cs_inventory);
747+
if (inv.type == MSG_TX) {
748+
if (!filterInventoryKnown.contains(inv.hash)) {
749+
setInventoryTxToSend.insert(inv.hash);
750+
}
751+
} else if (inv.type == MSG_BLOCK) {
752+
vInventoryBlockToSend.push_back(inv.hash);
753+
} else {
754+
vInventoryTierTwoToSend.emplace_back(inv);
743755
}
744756
}
745757

src/net_processing.cpp

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,23 +1945,16 @@ class CompareInvMempoolOrder
19451945
{
19461946
CTxMemPool *mp;
19471947
public:
1948-
CompareInvMempoolOrder(CTxMemPool *mempool)
1948+
CompareInvMempoolOrder(CTxMemPool *_mempool)
19491949
{
1950-
mp = mempool;
1950+
mp = _mempool;
19511951
}
19521952

1953-
bool operator()(const CInv &a, const CInv &b)
1953+
bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
19541954
{
1955-
if (a.type != MSG_TX && b.type != MSG_TX) {
1956-
return false;
1957-
} else {
1958-
if (a.type != MSG_TX) {
1959-
return true;
1960-
} else if (b.type != MSG_TX) {
1961-
return false;
1962-
}
1963-
return mp->CompareDepthAndScore(a.hash, b.hash);
1964-
}
1955+
/* As std::make_heap produces a max-heap, we want the entries with the
1956+
* fewest ancestors/highest fee to sort later. */
1957+
return mp->CompareDepthAndScore(*b, *a);
19651958
}
19661959
};
19671960

@@ -2088,36 +2081,68 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg
20882081
std::vector<CInv> vInv;
20892082
std::vector<CInv> vInvWait;
20902083
{
2084+
LOCK(pto->cs_inventory);
2085+
vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX));
2086+
2087+
// Add blocks
2088+
for (const uint256& hash : pto->vInventoryBlockToSend) {
2089+
vInv.emplace_back(CInv(MSG_BLOCK, hash));
2090+
if (vInv.size() == MAX_INV_SZ) {
2091+
connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
2092+
vInv.clear();
2093+
}
2094+
}
2095+
pto->vInventoryBlockToSend.clear();
2096+
2097+
// Add tier two INVs
2098+
for (const CInv& tInv : pto->vInventoryTierTwoToSend) {
2099+
vInv.emplace_back(tInv);
2100+
if (vInv.size() == MAX_INV_SZ) {
2101+
connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
2102+
vInv.clear();
2103+
}
2104+
}
2105+
pto->vInventoryTierTwoToSend.clear();
2106+
2107+
// Determine transactions to relay
20912108
bool fSendTrickle = pto->fWhitelisted;
20922109
if (pto->nNextInvSend < nNow) {
20932110
fSendTrickle = true;
2094-
// Use half the delay for outbound peers, as their is less privacy concern for them.
2111+
// Use half the delay for outbound peers, as there is less privacy concern for them.
20952112
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
20962113
}
2097-
LOCK(pto->cs_inventory);
2098-
if (fSendTrickle && pto->vInventoryToSend.size() > 1) {
2114+
if (fSendTrickle) {
2115+
// Produce a vector with all candidates for sending
2116+
std::vector<std::set<uint256>::iterator> vInvTx;
2117+
vInvTx.reserve(pto->setInventoryTxToSend.size());
2118+
for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
2119+
vInvTx.push_back(it);
2120+
}
20992121
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
2122+
// A heap is used so that not all items need sorting if only a few are being sent.
21002123
CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
2101-
std::stable_sort(pto->vInventoryToSend.begin(), pto->vInventoryToSend.end(), compareInvMempoolOrder);
2102-
}
2103-
vInv.reserve(std::min<size_t>(INVENTORY_BROADCAST_MAX, pto->vInventoryToSend.size()));
2104-
vInvWait.reserve(pto->vInventoryToSend.size());
2105-
for (const CInv& inv : pto->vInventoryToSend) {
2106-
if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash))
2107-
continue;
2108-
2124+
std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
21092125
// No reason to drain out at many times the network's capacity,
21102126
// especially since we have many peers and some will draw much shorter delays.
2111-
if (vInv.size() >= INVENTORY_BROADCAST_MAX || (inv.type == MSG_TX && !fSendTrickle)) {
2112-
vInvWait.push_back(inv);
2113-
continue;
2127+
unsigned int nRelayedTransactions = 0;
2128+
while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
2129+
// Fetch the top element from the heap
2130+
std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
2131+
std::set<uint256>::iterator it = vInvTx.back();
2132+
vInvTx.pop_back();
2133+
uint256 hash = *it;
2134+
// Remove it from the to-be-sent set
2135+
pto->setInventoryTxToSend.erase(it);
2136+
// Check if not in the filter already
2137+
if (pto->filterInventoryKnown.contains(hash)) {
2138+
continue;
2139+
}
2140+
// Send
2141+
vInv.emplace_back(CInv(MSG_TX, hash));
2142+
nRelayedTransactions++;
2143+
pto->filterInventoryKnown.insert(hash);
21142144
}
2115-
2116-
pto->filterInventoryKnown.insert(inv.hash);
2117-
2118-
vInv.push_back(inv);
21192145
}
2120-
pto->vInventoryToSend = vInvWait;
21212146
}
21222147
if (!vInv.empty())
21232148
connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));

0 commit comments

Comments
 (0)