upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp50
-rw-r--r--src/main.h9
-rw-r--r--src/net.cpp11
-rw-r--r--src/net.h26
4 files changed, 93 insertions, 3 deletions
diff --git a/src/main.cpp b/src/main.cpp
index b147ddd..4bc9c4d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -293,6 +293,43 @@ void AddOrphanTx(const CDataStream& vMsg)
293// grow this map until the node runs out of memory. Evict at random rather than 293// grow this map until the node runs out of memory. Evict at random rather than
294// in map order, so an attacker cannot keep its own orphans resident by 294// in map order, so an attacker cannot keep its own orphans resident by
295// choosing hashes that sort low. 295// choosing hashes that sort low.
296// Drop orphan blocks at random until we are back under the ceiling.
297//
298// Random rather than oldest-first, matching LimitOrphanTx below: there is no
299// arrival order recorded, and an attacker who knew the eviction rule could aim
300// at it. Dropping one costs nothing that matters -- if its parent ever shows
301// up, the block is requested again like any other.
302//
303// Both maps have to be kept in step, and the CBlock itself has to be deleted:
304// mapOrphanBlocks owns it.
305void LimitOrphanBlocks(unsigned int nMaxOrphans)
306{
307 while (mapOrphanBlocks.size() > nMaxOrphans)
308 {
309 uint256 randomhash;
310 for (int i = 0; i < 4; i++)
311 ((uint64*)&randomhash)[i] = GetRand(_UI64_MAX);
312 map<uint256, CBlock*>::iterator it = mapOrphanBlocks.lower_bound(randomhash);
313 if (it == mapOrphanBlocks.end())
314 it = mapOrphanBlocks.begin();
315
316 CBlock* pblock = it->second;
317 uint256 hashPrev = pblock->hashPrevBlock;
318
319 for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
320 mi != mapOrphanBlocksByPrev.upper_bound(hashPrev);)
321 {
322 if (mi->second == pblock)
323 mapOrphanBlocksByPrev.erase(mi++);
324 else
325 ++mi;
326 }
327
328 mapOrphanBlocks.erase(it);
329 delete pblock;
330 }
331}
332
296void LimitOrphanTx(unsigned int nMaxOrphans) 333void LimitOrphanTx(unsigned int nMaxOrphans)
297{ 334{
298 while (mapOrphanTransactions.size() > nMaxOrphans) 335 while (mapOrphanTransactions.size() > nMaxOrphans)
@@ -1468,9 +1505,16 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
1468 mapOrphanBlocks.insert(make_pair(hash, pblock)); 1505 mapOrphanBlocks.insert(make_pair(hash, pblock));
1469 mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock)); 1506 mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
1470 1507
1508 // Read what we need out of pblock before trimming: eviction picks at
1509 // random and is perfectly entitled to pick the block just inserted,
1510 // which would leave the GetOrphanRoot call below reading freed memory.
1511 uint256 hashOrphanRoot = GetOrphanRoot(pblock);
1512
1513 LimitOrphanBlocks(MAX_ORPHAN_BLOCKS);
1514
1471 // Ask this guy to fill in what we're missing 1515 // Ask this guy to fill in what we're missing
1472 if (pfrom) 1516 if (pfrom)
1473 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(pblock)); 1517 pfrom->PushGetBlocks(pindexBest, hashOrphanRoot);
1474 return true; 1518 return true;
1475 } 1519 }
1476 1520
@@ -2047,7 +2091,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2047 if (!fAskedForBlocks && !pfrom->fClient) 2091 if (!fAskedForBlocks && !pfrom->fClient)
2048 { 2092 {
2049 fAskedForBlocks = true; 2093 fAskedForBlocks = true;
2050 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0)); 2094 pfrom->PushGetBlocks(pindexBest, uint256(0));
2051 } 2095 }
2052 2096
2053 // Introduce ourselves and hand over the .btf peers that answered us. 2097 // Introduce ourselves and hand over the .btf peers that answered us.
@@ -2110,7 +2154,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2110 if (!fAlreadyHave) 2154 if (!fAlreadyHave)
2111 pfrom->AskFor(inv); 2155 pfrom->AskFor(inv);
2112 else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) 2156 else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
2113 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(mapOrphanBlocks[inv.hash])); 2157 pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
2114 } 2158 }
2115 } 2159 }
2116 2160
diff --git a/src/main.h b/src/main.h
index 2d5cb86..3bc1219 100644
--- a/src/main.h
+++ b/src/main.h
@@ -18,6 +18,13 @@ static const unsigned int MAX_SIZE = 0x02000000;
18// Ceiling on transactions held waiting for a parent that has not arrived. 18// Ceiling on transactions held waiting for a parent that has not arrived.
19// They cost a peer nothing to send and are never validated, only stored. 19// They cost a peer nothing to send and are never validated, only stored.
20static const unsigned int MAX_ORPHAN_TRANSACTIONS = 100; 20static const unsigned int MAX_ORPHAN_TRANSACTIONS = 100;
21// Blocks held waiting for a parent that has not arrived. Orphan transactions
22// have had a ceiling since 0.1.0 and orphan blocks never did: one whose parent
23// never turns up stays in memory for the life of the process, and a peer can
24// send an unlimited number of them by inventing the previous-block hash. They
25// are not free either -- each holds a whole block. A production node was
26// seeing 1287 of them against 4117 blocks processed.
27static const unsigned int MAX_ORPHAN_BLOCKS = 200;
21// Signature operations allowed in one block. Size alone does not bound what a 28// Signature operations allowed in one block. Size alone does not bound what a
22// block costs to validate: every OP_CHECKSIG is an elliptic-curve verification, 29// block costs to validate: every OP_CHECKSIG is an elliptic-curve verification,
23// so a block within the size limit could still hold millions of them and take 30// so a block within the size limit could still hold millions of them and take
@@ -125,6 +132,8 @@ void PrintBlockTree();
125void AddOrphanTx(const CDataStream& vMsg); 132void AddOrphanTx(const CDataStream& vMsg);
126void EraseOrphanTx(uint256 hash); 133void EraseOrphanTx(uint256 hash);
127void LimitOrphanTx(unsigned int nMaxOrphans); 134void LimitOrphanTx(unsigned int nMaxOrphans);
135// Same ceiling idea for orphan blocks, which never had one.
136void LimitOrphanBlocks(unsigned int nMaxOrphans);
128// nThreadId is 1..N, only for logging: it makes each miner's lines 137// nThreadId is 1..N, only for logging: it makes each miner's lines
129// distinguishable, which the dedup filter would otherwise collapse into one. 138// distinguishable, which the dedup filter would otherwise collapse into one.
130bool BitcoinMiner(int nThreadId = 1); 139bool BitcoinMiner(int nThreadId = 1);
diff --git a/src/net.cpp b/src/net.cpp
index ac8df82..37c081f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -181,6 +181,17 @@ bool AnySubscribed(unsigned int nChannel)
181 return false; 181 return false;
182} 182}
183 183
184void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
185{
186 // Filter out duplicate requests.
187 if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd)
188 return;
189 pindexLastGetBlocksBegin = pindexBegin;
190 hashLastGetBlocksEnd = hashEnd;
191
192 PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd);
193}
194
184bool CNode::IsSubscribed(unsigned int nChannel) 195bool CNode::IsSubscribed(unsigned int nChannel)
185{ 196{
186 if (nChannel >= vfSubscribe.size()) 197 if (nChannel >= vfSubscribe.size())
diff --git a/src/net.h b/src/net.h
index 831a880..5e828c3 100644
--- a/src/net.h
+++ b/src/net.h
@@ -7,6 +7,9 @@ class CAddress;
7class CInv; 7class CInv;
8class CRequestTracker; 8class CRequestTracker;
9class CNode; 9class CNode;
10// Defined in main.h, which is included after this file. Only ever used here as
11// a pointer, so the forward declaration is enough.
12class CBlockIndex;
10 13
11 14
12 15
@@ -533,6 +536,11 @@ public:
533 // Last "btfpeers" we accepted from this node, to rate-limit the exchange. 536 // Last "btfpeers" we accepted from this node, to rate-limit the exchange.
534 int64 nLastPexRecv; 537 int64 nLastPexRecv;
535 538
539 // Last getblocks we sent this peer, so we do not ask the same question
540 // over and over. See CNode::PushGetBlocks.
541 CBlockIndex* pindexLastGetBlocksBegin;
542 uint256 hashLastGetBlocksEnd;
543
536 // Liveness. A peer whose network path dies silently -- NAT drops an idle 544 // Liveness. A peer whose network path dies silently -- NAT drops an idle
537 // mapping, a relay restarts, a route changes -- never sends FIN, so the 545 // mapping, a relay restarts, a route changes -- never sends FIN, so the
538 // socket stays readable-never and the node simply stops hearing from it 546 // socket stays readable-never and the node simply stops hearing from it
@@ -565,6 +573,8 @@ public:
565 nRefCount = 0; 573 nRefCount = 0;
566 nReleaseTime = 0; 574 nReleaseTime = 0;
567 nLastPexRecv = 0; 575 nLastPexRecv = 0;
576 pindexLastGetBlocksBegin = NULL;
577 hashLastGetBlocksEnd = 0;
568 nTimeConnected = GetTime(); 578 nTimeConnected = GetTime();
569 nLastSend = 0; 579 nLastSend = 0;
570 nLastRecv = 0; 580 nLastRecv = 0;
@@ -717,6 +727,22 @@ public:
717 727
718 728
719 729
730 // Ask this peer to fill in blocks we are missing, but only once per
731 // question.
732 //
733 // Every orphan block fires one of these, and an inv for a block we already
734 // hold as an orphan fires another. A node that is missing part of the
735 // chain therefore asks the same peer the same thing repeatedly: measured
736 // on a production node, 1287 orphan blocks out of 4117 processed, and 97
737 // "already have block" from the answers coming back more than once. The
738 // duplicate answers create more orphans, which fire more requests.
739 //
740 // Remembering the last question asked breaks that loop. Same fix Bitcoin
741 // made in e2c2648c1.
742 // Body lives in net.cpp: it builds a CBlockLocator, which main.h defines,
743 // and main.h is included after this file.
744 void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);
745
720 void PushMessage(const char* pszCommand) 746 void PushMessage(const char* pszCommand)
721 { 747 {
722 try 748 try