diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -3,3 +3,9 @@
This release includes the following features and fixes:
+Seeder
+------
+ - Seeder now requires nodes to have the most recent checkpoint in its chain
+ in order to be considered a good seed.
+ - Nodes that do not have the most recent checkpoint in its chain are
+ temporally banned.
diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp
--- a/src/seeder/bitcoin.cpp
+++ b/src/seeder/bitcoin.cpp
@@ -113,18 +113,67 @@
if (strCommand == "verack") {
vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION));
- // fprintf(stdout, "\n%s: version %i\n", ToString(you).c_str(),
- // nVersion);
- if (vAddr) {
- BeginMessage("getaddr");
- EndMessage();
- doneAfter = time(nullptr) + GetTimeout();
- } else {
- doneAfter = time(nullptr) + 1;
- }
+ // Create a getheaders message
+ BeginMessage("getheaders");
+ // Locator hash; Seeder requests the headers after the latest checkpoint
+ uint256 seederCheckpoint =
+ Params().Checkpoints().mapCheckpoints.rbegin()->second;
+ std::vector hashStop(32, 0);
+ uint8_t numberOfLocatorHashes = 1;
+ vSend << PROTOCOL_VERSION << numberOfLocatorHashes << seederCheckpoint
+ << uint256(hashStop);
+ EndMessage();
+ // Update socket timeout
+ doneAfter = time(nullptr) + GetTimeout();
return false;
}
+ if (strCommand == NetMsgType::HEADERS) {
+ uint64_t nHeaders = ReadCompactSize(recv);
+ if (nHeaders == 0) {
+ // fprintf(stderr,
+ //"Peer (%s) did not find seeder checkpoint, banned.\n",
+ // ToString(you).c_str());
+ ban = 100000;
+ return true;
+ }
+ CBlockHeader header;
+ recv >> header;
+ /**
+ * If the node has this checkpoint then it will return the first 2000
+ * block headers it knows that come after the checkpoint. This means
+ * the hashPrevBlock of the first header received should be the hash of
+ * the checkpoint if the node is following the correct chain.
+ */
+ if (Params().Checkpoints().mapCheckpoints.rbegin()->second ==
+ header.hashPrevBlock) {
+ // fprintf(stderr, "Peer (%s) found seeder checkpoint.\n",
+ // ToString(you).c_str());
+ // fprintf(stdout, "\n%s: version %i\n", ToString(you).c_str(),
+ // nVersion);
+ if (vAddr) {
+ BeginMessage("getaddr");
+ EndMessage();
+ doneAfter = time(nullptr) + GetTimeout();
+ } else {
+ doneAfter = time(nullptr) + 1;
+ }
+ return false;
+ }
+
+ /**
+ * Conversely, if hasPrevBlock is not the checkpoint, then the node is
+ * not following the correct chain and should be banned.
+ * This should probably be a longer lasting ban in the future.
+ */
+ // fprintf(stderr,
+ //"Peer (%s) returned a header the does not come after the "
+ //"checkpoint and is therefore on the wrong chain, banned.\n",
+ // ToString(you).c_str());
+ ban = 100000;
+ return true;
+ }
+
if (strCommand == "addr" && vAddr) {
std::vector vAddrNew;
recv >> vAddrNew;
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
@@ -5,6 +5,7 @@
#define BOOST_TEST_MODULE Bitcoin Seeder Test Suite
#include
+#include
#include
#include
#include
@@ -134,4 +135,70 @@
}
}
+static const uint8_t SEND_WRONG_CHAIN_HEADER = 0;
+static const uint8_t SEND_GOOD_HEADER = 1;
+static const uint8_t SEND_NO_HEADERS = 2;
+static const uint8_t HEADERS_TXN_COUNT = 0;
+
+static CDataStream CreateHeadersMessage(uint8_t headersMessageContent,
+ int32_t nVersion = INIT_PROTO_VERSION,
+ uint32_t timeStamp = 0,
+ uint32_t bits = 0, uint32_t nonce = 0) {
+ CDataStream payload(SER_NETWORK, 0);
+ payload.SetVersion(nVersion);
+ uint8_t numberOfHeaders = headersMessageContent == SEND_NO_HEADERS ? 0 : 1;
+ payload << numberOfHeaders;
+ if (headersMessageContent == SEND_NO_HEADERS) {
+ return payload;
+ }
+ // The actual header can be garbage data, Seeder only cares that it got
+ // a specific type of message, the size, and the hash of the previous block.
+ uint256 merkleRoot = Hash(payload.begin(), payload.end());
+ uint256 previousBlockHash;
+ if (headersMessageContent == SEND_WRONG_CHAIN_HEADER) {
+ previousBlockHash = Hash(payload.begin(), payload.end());
+ } else {
+ previousBlockHash =
+ Params().Checkpoints().mapCheckpoints.rbegin()->second;
+ }
+ payload << nVersion;
+ // prev_block
+ payload << previousBlockHash;
+ // merkle_root
+ payload << merkleRoot;
+ // timestamp
+ payload << timeStamp;
+ // bits
+ payload << bits;
+ // nonce
+ payload << nonce;
+ // txn_count
+ payload << HEADERS_TXN_COUNT;
+ return payload;
+}
+
+BOOST_AUTO_TEST_CASE(seeder_node_headers_test_happy_path) {
+ // Happy path
+ CDataStream headersMessage = CreateHeadersMessage(SEND_GOOD_HEADER);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::AwaitingMessages);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(seeder_node_headers_wrong_chain_test) {
+ // Seeder receives a HEADERS message following the wrong chain
+ CDataStream headersMessage = CreateHeadersMessage(SEND_WRONG_CHAIN_HEADER);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
+BOOST_AUTO_TEST_CASE(seeder_node_empty_headers_test) {
+ // Seeder receives an empty HEADERS message
+ CDataStream headersMessage = CreateHeadersMessage(SEND_NO_HEADERS);
+ testNode->TestProcessMessage(NetMsgType::HEADERS, headersMessage,
+ PeerMessagingState::Finished);
+ BOOST_CHECK_EQUAL(testNode->GetBan(), 100000);
+}
+
BOOST_AUTO_TEST_SUITE_END()