Skip to content

Commit a029e18

Browse files
committed
Use rolling bloom filter of recent block tx's for AlreadyHave() check
In order to determine whether to download or process a relayed transaction, we try to determine if we already have the transaction, either in the mempool, in our recently rejected filter, in our orphan pool, or already confirmed in the chain itself. Prior to this commit, the heuristic for checking the chain is based on whether there's an output corresponding to the 0- or 1-index vout in our coin cache. While that is a quick check, it is very imprecise (say if those outputs were already spent in a block) -- we can do better by just keeping a rolling bloom filter of the transactions in recent blocks, which will capture the case of a transaction which has been confirmed and then fully spent already. To avoid relay problems for transactions which have been included in a recent block but then reorged out of the chain, we clear the bloom filter whenever a block is disconnected.
1 parent 0deba68 commit a029e18

File tree

2 files changed

+69
-25
lines changed

2 files changed

+69
-25
lines changed

src/net_processing.cpp

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ namespace {
148148
std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main);
149149
uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main);
150150

151+
/*
152+
* Filter for transactions that have been recently confirmed.
153+
* We use this to avoid requesting transactions that have already been
154+
* confirnmed.
155+
*/
156+
RecursiveMutex g_cs_recent_confirmed_transactions;
157+
std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions GUARDED_BY(g_cs_recent_confirmed_transactions);
158+
151159
/** Blocks that are in flight, and that are in the queue to be downloaded. */
152160
struct QueuedBlock {
153161
uint256 hash;
@@ -1116,6 +1124,16 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CS
11161124
// Initialize global variables that cannot be constructed at startup.
11171125
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
11181126

1127+
// Blocks don't typically have more than 4000 transactions, so this should
1128+
// be at least six blocks (~1 hr) worth of transactions that we can store.
1129+
// If the number of transactions appearing in a block goes up, or if we are
1130+
// seeing getdata requests more than an hour after initial announcement, we
1131+
// can increase this number.
1132+
// The false positive rate of 1/1M should come out to less than 1
1133+
// transaction per day that would be inadvertently ignored (which is the
1134+
// same probability that we have in the reject filter).
1135+
g_recent_confirmed_transactions.reset(new CRollingBloomFilter(24000, 0.000001));
1136+
11191137
const Consensus::Params& consensusParams = Params().GetConsensus();
11201138
// Stale tip checking and peer eviction are on two different timers, but we
11211139
// don't want them to get out of sync due to drift in the scheduler, so we
@@ -1129,36 +1147,59 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CS
11291147
* Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
11301148
* block. Also save the time of the last tip update.
11311149
*/
1132-
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
1133-
LOCK(g_cs_orphans);
1150+
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted)
1151+
{
1152+
{
1153+
LOCK(g_cs_orphans);
11341154

1135-
std::vector<uint256> vOrphanErase;
1155+
std::vector<uint256> vOrphanErase;
11361156

1137-
for (const CTransactionRef& ptx : pblock->vtx) {
1138-
const CTransaction& tx = *ptx;
1157+
for (const CTransactionRef& ptx : pblock->vtx) {
1158+
const CTransaction& tx = *ptx;
11391159

1140-
// Which orphan pool entries must we evict?
1141-
for (const auto& txin : tx.vin) {
1142-
auto itByPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
1143-
if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
1144-
for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
1145-
const CTransaction& orphanTx = *(*mi)->second.tx;
1146-
const uint256& orphanHash = orphanTx.GetHash();
1147-
vOrphanErase.push_back(orphanHash);
1160+
// Which orphan pool entries must we evict?
1161+
for (const auto& txin : tx.vin) {
1162+
auto itByPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
1163+
if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
1164+
for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
1165+
const CTransaction& orphanTx = *(*mi)->second.tx;
1166+
const uint256& orphanHash = orphanTx.GetHash();
1167+
vOrphanErase.push_back(orphanHash);
1168+
}
11481169
}
11491170
}
1150-
}
11511171

1152-
// Erase orphan transactions included or precluded by this block
1153-
if (vOrphanErase.size()) {
1154-
int nErased = 0;
1155-
for (const uint256& orphanHash : vOrphanErase) {
1156-
nErased += EraseOrphanTx(orphanHash);
1172+
// Erase orphan transactions included or precluded by this block
1173+
if (vOrphanErase.size()) {
1174+
int nErased = 0;
1175+
for (const uint256& orphanHash : vOrphanErase) {
1176+
nErased += EraseOrphanTx(orphanHash);
1177+
}
1178+
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
1179+
}
1180+
1181+
g_last_tip_update = GetTime();
1182+
}
1183+
{
1184+
LOCK(g_cs_recent_confirmed_transactions);
1185+
for (const auto ptx : pblock->vtx) {
1186+
g_recent_confirmed_transactions->insert(ptx->GetHash());
11571187
}
1158-
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
11591188
}
1189+
}
11601190

1161-
g_last_tip_update = GetTime();
1191+
void PeerLogicValidation::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex)
1192+
{
1193+
// To avoid relay problems with transactions that were previously
1194+
// confirmed, clear our filter of recently confirmed transactions whenever
1195+
// there's a reorg.
1196+
// This means that in a 1-block reorg (where 1 block is disconnected and
1197+
// then another block reconnected), our filter will drop to having only one
1198+
// block's worth of transactions in it, but that should be fine, since
1199+
// presumably the most common case of relaying a confirmed transaction
1200+
// should be just after a new block containing it is found.
1201+
LOCK(g_cs_recent_confirmed_transactions);
1202+
g_recent_confirmed_transactions->reset();
11621203
}
11631204

11641205
// All of the following cache a recent block, and are protected by cs_most_recent_block
@@ -1311,12 +1352,14 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
13111352
LOCK(g_cs_orphans);
13121353
if (mapOrphanTransactions.count(inv.hash)) return true;
13131354
}
1314-
const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
1355+
1356+
{
1357+
LOCK(g_cs_recent_confirmed_transactions);
1358+
if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
1359+
}
13151360

13161361
return recentRejects->contains(inv.hash) ||
1317-
mempool.exists(inv.hash) ||
1318-
coins_cache.HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
1319-
coins_cache.HaveCoinInCache(COutPoint(inv.hash, 1));
1362+
mempool.exists(inv.hash);
13201363
}
13211364
case MSG_BLOCK:
13221365
case MSG_WITNESS_BLOCK:

src/net_processing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class PeerLogicValidation final : public CValidationInterface, public NetEventsI
3333
* Overridden from CValidationInterface.
3434
*/
3535
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
36+
void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override;
3637
/**
3738
* Overridden from CValidationInterface.
3839
*/

0 commit comments

Comments
 (0)