aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2015-04-08 11:20:00 -0700
committerPieter Wuille <pieter.wuille@gmail.com>2015-12-11 22:20:38 +0100
commit5400ef6bcb9d243b2b21697775aa6491115420f3 (patch)
treebb8bb37f0db1e2a48318f64fb42de0a95d5f997b
parent9ee02cf564d1ce79d2981899cb4d38c914210dc7 (diff)
Replace trickle nodes with per-node/message Poisson delays
We used to have a trickle node, a node which was chosen in each iteration of the send loop that was privileged and allowed to send out queued up non-time critical messages. Since the removal of the fixed sleeps in the network code, this resulted in fast and attackable treatment of such broadcasts. This pull request changes the 3 remaining trickle use cases by random delays: * Local address broadcast (while also removing the the wiping of the seen filter) * Address relay * Inv relay (for transactions; blocks are always relayed immediately) The code is based on older commits by Patrick Strateman.
-rw-r--r--src/main.cpp34
-rw-r--r--src/main.h11
-rw-r--r--src/net.cpp16
-rw-r--r--src/net.h8
-rw-r--r--src/test/DoS_tests.cpp14
5 files changed, 47 insertions, 36 deletions
diff --git a/src/main.cpp b/src/main.cpp
index d2e736d421..41fc0b8098 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5326,7 +5326,7 @@ bool ProcessMessages(CNode* pfrom)
}
-bool SendMessages(CNode* pto, bool fSendTrickle)
+bool SendMessages(CNode* pto)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
{
@@ -5368,28 +5368,17 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
return true;
// Address refresh broadcast
- static int64_t nLastRebroadcast;
- if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- {
- // Periodically clear addrKnown to allow refresh broadcasts
- if (nLastRebroadcast)
- pnode->addrKnown.reset();
-
- // Rebroadcast our address
- AdvertizeLocal(pnode);
- }
- if (!vNodes.empty())
- nLastRebroadcast = GetTime();
+ int64_t nNow = GetTimeMicros();
+ if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ AdvertizeLocal(pto);
+ pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
//
// Message: addr
//
- if (fSendTrickle)
- {
+ if (pto->nNextAddrSend < nNow) {
+ pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
@@ -5563,8 +5552,13 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vector<CInv> vInv;
vector<CInv> vInvWait;
{
+ bool fSendTrickle = pto->fWhitelisted;
+ if (pto->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL);
+ }
LOCK(pto->cs_inventory);
- vInv.reserve(pto->vInventoryToSend.size());
+ vInv.reserve(std::min<size_t>(1000, pto->vInventoryToSend.size()));
vInvWait.reserve(pto->vInventoryToSend.size());
BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)
{
@@ -5604,7 +5598,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->PushMessage(NetMsgType::INV, vInv);
// Detect whether we're stalling
- int64_t nNow = GetTimeMicros();
+ nNow = GetTimeMicros();
if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
diff --git a/src/main.h b/src/main.h
index 19623f4d96..25a0063873 100644
--- a/src/main.h
+++ b/src/main.h
@@ -87,6 +87,14 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
+/** Average delay between local address broadcasts in seconds. */
+static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60;
+/** Average delay between peer address broadcasts in seconds. */
+static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
+/** Average delay between trickled inventory broadcasts in seconds.
+ * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */
+static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5;
+
static const unsigned int DEFAULT_LIMITFREERELAY = 15;
static const bool DEFAULT_RELAYPRIORITY = true;
@@ -197,9 +205,8 @@ bool ProcessMessages(CNode* pfrom);
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
- * @param[in] fSendTrickle When true send the trickled data, otherwise trickle the data until true.
*/
-bool SendMessages(CNode* pto, bool fSendTrickle);
+bool SendMessages(CNode* pto);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Try to detect Partition (network isolation) attacks against us */
diff --git a/src/net.cpp b/src/net.cpp
index c5e7ece79d..e0d96a2dc8 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -36,6 +36,8 @@
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
+#include <math.h>
+
// Dump addresses to peers.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900
@@ -1733,11 +1735,6 @@ void ThreadMessageHandler()
}
}
- // Poll the connected nodes for messages
- CNode* pnodeTrickle = NULL;
- if (!vNodesCopy.empty())
- pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
-
bool fSleep = true;
BOOST_FOREACH(CNode* pnode, vNodesCopy)
@@ -1768,7 +1765,7 @@ void ThreadMessageHandler()
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
- g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted);
+ g_signals.SendMessages(pnode);
}
boost::this_thread::interruption_point();
}
@@ -2384,6 +2381,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
nStartingHeight = -1;
filterInventoryKnown.reset();
fGetAddr = false;
+ nNextLocalAddrSend = 0;
+ nNextAddrSend = 0;
+ nNextInvSend = 0;
fRelayTxes = false;
pfilter = new CBloomFilter();
nPingNonceSent = 0;
@@ -2634,3 +2634,7 @@ void DumpBanlist()
LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
}
+
+int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
+ return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+}
diff --git a/src/net.h b/src/net.h
index 3ed438605e..bc64571aeb 100644
--- a/src/net.h
+++ b/src/net.h
@@ -113,7 +113,7 @@ struct CNodeSignals
{
boost::signals2::signal<int ()> GetHeight;
boost::signals2::signal<bool (CNode*), CombinerAll> ProcessMessages;
- boost::signals2::signal<bool (CNode*, bool), CombinerAll> SendMessages;
+ boost::signals2::signal<bool (CNode*), CombinerAll> SendMessages;
boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
boost::signals2::signal<void (NodeId)> FinalizeNode;
};
@@ -391,6 +391,8 @@ public:
CRollingBloomFilter addrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
+ int64_t nNextAddrSend;
+ int64_t nNextLocalAddrSend;
// inventory based relay
CRollingBloomFilter filterInventoryKnown;
@@ -398,6 +400,7 @@ public:
CCriticalSection cs_inventory;
std::set<uint256> setAskFor;
std::multimap<int64_t, CInv> mapAskFor;
+ int64_t nNextInvSend;
// Used for headers announcements - unfiltered blocks to relay
// Also protected by cs_inventory
std::vector<uint256> vBlockHashesToAnnounce;
@@ -791,4 +794,7 @@ public:
void DumpBanlist();
+/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
+int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds);
+
#endif // BITCOIN_NET_H
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index da296a0461..51d296502e 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
- SendMessages(&dummyNode1, false);
+ SendMessages(&dummyNode1);
BOOST_CHECK(CNode::IsBanned(addr1));
BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
@@ -57,11 +57,11 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
dummyNode2.nVersion = 1;
Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2, false);
+ SendMessages(&dummyNode2);
BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be
Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2, false);
+ SendMessages(&dummyNode2);
BOOST_CHECK(CNode::IsBanned(addr2));
}
@@ -73,13 +73,13 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100);
- SendMessages(&dummyNode1, false);
+ SendMessages(&dummyNode1);
BOOST_CHECK(!CNode::IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 10);
- SendMessages(&dummyNode1, false);
+ SendMessages(&dummyNode1);
BOOST_CHECK(!CNode::IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 1);
- SendMessages(&dummyNode1, false);
+ SendMessages(&dummyNode1);
BOOST_CHECK(CNode::IsBanned(addr1));
mapArgs.erase("-banscore");
}
@@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
dummyNode.nVersion = 1;
Misbehaving(dummyNode.GetId(), 100);
- SendMessages(&dummyNode, false);
+ SendMessages(&dummyNode);
BOOST_CHECK(CNode::IsBanned(addr));
SetMockTime(nStartTime+60*60);