Page MenuHomePhabricator

D5380.diff
No OneTemporary

D5380.diff

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

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)

Event Timeline