Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12945019
D5380.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Subscribers
None
D5380.diff
View Options
diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -5,3 +5,10 @@
<https://download.bitcoinabc.org/0.22.10/>
This release includes the following features and fixes:
+
+# Seeder
+
+- Seeder now requires nodes to have the block after the most recent checkpoint
+ in its chain in order to be considered a good seed.
+- Nodes that are undergoing IBD are not considered good seeds until IBD is
+ completed.
diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h
--- a/src/seeder/bitcoin.h
+++ b/src/seeder/bitcoin.h
@@ -25,6 +25,13 @@
Finished,
};
+enum class CheckpointState {
+ HasLatest,
+ IncompleteChain,
+ NotChecked,
+ NotFound,
+};
+
namespace {
class CSeederNodeTest;
}
@@ -45,6 +52,7 @@
int ban;
int64_t doneAfter;
CAddress you;
+ CheckpointState checkpointState;
int GetTimeout() { return you.IsTor() ? 120 : 30; }
diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp
--- a/src/seeder/bitcoin.cpp
+++ b/src/seeder/bitcoin.cpp
@@ -13,6 +13,7 @@
#include <serialize.h>
#include <uint256.h>
#include <util/time.h>
+#include <validation.h>
#include <algorithm>
@@ -101,13 +102,69 @@
// ToString(you),
// addr.ToString(), (int)(vAddr->size()));
if (vAddr->size() > ADDR_SOFT_CAP) {
- doneAfter = 1;
- return PeerMessagingState::Finished;
+ // Seeder is finished with a node after it has received
+ // ADDR_SOFT_CAP addrs from a node AND after the seeder has
+ // checked the node's chain
+ if (checkpointState != CheckpointState::NotChecked) {
+ // Note that we do not ban until after addrs are received
+ // so that we can still crawl the network effectively.
+ if (checkpointState == CheckpointState::NotFound) {
+ ban = 100000;
+ }
+ doneAfter = checkpointState == CheckpointState::HasLatest;
+ return PeerMessagingState::Finished;
+ }
+ break;
}
}
return PeerMessagingState::AwaitingMessages;
}
+ // A node in the process of IBD-ing will not send this message and
+ // therefore will timeout waiting for the checkpoint.
+ if (strCommand == NetMsgType::HEADERS && vAddr) {
+ std::vector<CBlockHeader> headers;
+ recv >> headers;
+ const CChainParams &chainParams = Params();
+ doneAfter = 0;
+ checkpointState = CheckpointState::NotFound;
+
+ if (headers.size() > MAX_HEADERS_RESULTS) {
+ tfm::format(std::cout,
+ "%s: Misbehaving: oversized HEADERS message\n",
+ ToString(you));
+ ban = 100000;
+ return PeerMessagingState::Finished;
+ }
+
+ if (headers.size() == 0) {
+ // This is an ambiguous situation where either the peer only has
+ // the genesis block or has exactly up to the checkpoint. Either
+ // way, the peer should be marked bad but not banned.
+ checkpointState = CheckpointState::IncompleteChain;
+ } else if (chainParams.Checkpoints().mapCheckpoints.rbegin()->second ==
+ headers[0].hashPrevBlock) {
+ // The hashPrevBlock of the first header received should be the hash
+ // of the checkpoint if the node is following the correct chain.
+ doneAfter = GetTime() + GetTimeout();
+ checkpointState = CheckpointState::HasLatest;
+ }
+
+ // Seeder is finished with a node after it has received ADDR_SOFT_CAP
+ // addrs from a node AND after the seeder has checked the node's chain
+ if (vAddr->size() > ADDR_SOFT_CAP) {
+ // Note that we do not ban until after addrs are received
+ // so that we can still crawl the network effectively.
+ if (checkpointState == CheckpointState::NotFound) {
+ ban = 100000;
+ }
+ doneAfter = checkpointState == CheckpointState::HasLatest;
+ return PeerMessagingState::Finished;
+ }
+
+ return PeerMessagingState::AwaitingMessages;
+ }
+
return PeerMessagingState::AwaitingMessages;
}
@@ -175,7 +232,8 @@
CSeederNode::CSeederNode(const CService &ip, std::vector<CAddress> *vAddrIn)
: sock(INVALID_SOCKET), vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0),
nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0),
- doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK)) {
+ doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK)),
+ checkpointState(CheckpointState::NotChecked) {
if (GetTime() > 1329696000) {
vSend.SetVersion(209);
vRecv.SetVersion(209);
@@ -280,5 +338,5 @@
}
close(sock);
sock = INVALID_SOCKET;
- return (ban == 0) && res;
+ return (ban == 0) && res && (checkpointState == CheckpointState::HasLatest);
}
diff --git a/src/seeder/test/p2p_messaging_tests.cpp b/src/seeder/test/p2p_messaging_tests.cpp
--- a/src/seeder/test/p2p_messaging_tests.cpp
+++ b/src/seeder/test/p2p_messaging_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <hash.h>
#include <protocol.h>
#include <seeder/bitcoin.h>
#include <seeder/db.h>
@@ -10,6 +11,7 @@
#include <serialize.h>
#include <streams.h>
#include <util/system.h>
+#include <validation.h>
#include <version.h>
#include <boost/test/unit_test.hpp>
@@ -63,7 +65,7 @@
int32_t start_height, uint32_t nVersion,
uint64_t nonce = 0,
std::string user_agent = "/bitcoin-cash-seeder:0.15/") {
- CDataStream payload(SER_NETWORK, 0);
+ CDataStream payload(SER_NETWORK, PROTOCOL_VERSION);
payload.SetVersion(nVersion);
ServiceFlags serviceflags = ServiceFlags(NODE_NETWORK);
payload << nVersion << uint64_t(serviceflags) << now << addrTo << addrFrom
@@ -91,7 +93,7 @@
}
BOOST_AUTO_TEST_CASE(process_verack_msg) {
- CDataStream verackMessage(SER_NETWORK, 0);
+ CDataStream verackMessage(SER_NETWORK, PROTOCOL_VERSION);
verackMessage.SetVersion(INIT_PROTO_VERSION);
testNode->TestProcessMessage(NetMsgType::VERACK, verackMessage,
PeerMessagingState::AwaitingMessages);
@@ -120,7 +122,7 @@
static CDataStream CreateAddrMessage(std::vector<CAddress> sendAddrs,
uint32_t nVersion = INIT_PROTO_VERSION) {
- CDataStream payload(SER_NETWORK, 0);
+ CDataStream payload(SER_NETWORK, PROTOCOL_VERSION);
payload.SetVersion(nVersion);
payload << sendAddrs;
return payload;
@@ -143,7 +145,7 @@
sendAddrs.resize(1);
addrMessage = CreateAddrMessage(sendAddrs);
testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
- PeerMessagingState::Finished);
+ PeerMessagingState::AwaitingMessages);
BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
// Test the seeder's behavior after ADDR_SOFT_CAP addrs
@@ -153,10 +155,177 @@
sendAddrs.resize(i, sendAddrs[0]);
addrMessage = CreateAddrMessage(sendAddrs);
testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
- PeerMessagingState::Finished);
+ PeerMessagingState::AwaitingMessages);
BOOST_CHECK_EQUAL(expectedSize, vAddr.size());
++expectedSize;
}
}
+static CDataStream CreateHeadersMessage(int numHeaders = 1) {
+ CDataStream payload(SER_NETWORK, PROTOCOL_VERSION);
+
+ BlockHash previousBlockHash =
+ Params().Checkpoints().mapCheckpoints.rbegin()->second;
+
+ // Treat negative numHeaders as the wrong chain
+ if (numHeaders < 0) {
+ numHeaders *= -1;
+ previousBlockHash = BlockHash();
+ }
+
+ CBlockHeader header;
+ header.hashPrevBlock = previousBlockHash;
+ std::vector<CBlock> headers(numHeaders, CBlock(header));
+ payload << headers;
+ return payload;
+}
+
+BOOST_AUTO_TEST_CASE(addrs_before_checkpoint) {
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+ CDataStream headersMessage = CreateHeadersMessage();
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(wrong_chain_before_addrs) {
+ // Happy path except the addrs come after the headers from the wrong chain
+ CDataStream headersMessage = CreateHeadersMessage(-1);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::AwaitingMessages);
+
+ // Banning should not occur until after addrs are finished being received.
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
+BOOST_AUTO_TEST_CASE(headers_wrong_chain_after_addrs) {
+ // Seeder receives a HEADERS message obviously following the wrong chain
+ // This type of node should be banned after filling vAddr.
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+
+ // Banning should not occur until after addrs are finished being received.
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+ CDataStream headersMessage = CreateHeadersMessage(-1);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
+BOOST_AUTO_TEST_CASE(too_many_headers) {
+ // Seeder receives a HEADERS message with too many headers
+ // This is a misbehaving node and should be banned immediately.
+ CDataStream headersMessage = CreateHeadersMessage(MAX_HEADERS_RESULTS + 1);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
+BOOST_AUTO_TEST_CASE(empty_headers_test) {
+ /**
+ * Seeder receives an empty HEADERS message after filling vAddr
+ * Peer node did not have the checkpoint block requested by the seeder.
+ * Peer only has up to the checkpoint or only has the genesis block.
+ * Since there is no way to tell the difference do not ban.
+ */
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+ CDataStream headersMessage = CreateHeadersMessage(0);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(checkpoint_before_addrs) {
+ CDataStream headersMessage = CreateHeadersMessage();
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(addrs_before_and_after_checkpoint) {
+ // Addrs come before and after the headers message. The first addr does not
+ // fill vAddr, so the seeder should wait for more addrs before it is
+ // finished with the node
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP - 1, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP, vAddr.size());
+
+ CDataStream headersMessage = CreateHeadersMessage();
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+
+ sendAddrs.resize(1);
+ addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(addrs_before_and_after_wrong_chain) {
+ // Addrs come before and after the headers message. The first addr does not
+ // fill vAddr, so the seeder should wait for more addrs before it is
+ // finished with the node. The node is on the wrong chain
+ std::vector<CAddress> sendAddrs(ADDR_SOFT_CAP - 1, vAddr[0]);
+ CDataStream addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(1, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP, vAddr.size());
+
+ // Banning should not occur until after addrs are finished being received.
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+ CDataStream headersMessage = CreateHeadersMessage(-1);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+
+ sendAddrs.resize(1);
+ addrMessage = CreateAddrMessage(sendAddrs);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP, vAddr.size());
+ testNode->TestProcessMessage(NetMsgType::ADDR, addrMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(ADDR_SOFT_CAP + 1, vAddr.size());
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
BOOST_AUTO_TEST_SUITE_END()
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Feb 6, 17:08 (17 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082753
Default Alt Text
D5380.diff (15 KB)
Attached To
D5380: [seeder] Require good seeds to have latest checkpoint
Event Timeline
Log In to Comment