diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp --- a/src/seeder/bitcoin.cpp +++ b/src/seeder/bitcoin.cpp @@ -4,6 +4,8 @@ #include +#include +#include #include #include #include @@ -164,6 +166,43 @@ 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()); + return false; + } + + /** + * Conversely, if hasPrevBlock is not the checkpoint, then the node is + * not following the correct chain and should be banned. + */ + // 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()); + // This should probably be a longer lasting band in the future + ban = 100000; + return true; + } + return false; } diff --git a/src/seeder/test/seeder_tests.cpp b/src/seeder/test/seeder_tests.cpp --- a/src/seeder/test/seeder_tests.cpp +++ b/src/seeder/test/seeder_tests.cpp @@ -27,6 +27,10 @@ BOOST_AUTO_TEST_SUITE(seeder_tests) static const unsigned short SERVICE_PORT = 18444; +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 const uint32_t VERACK_PAYLOAD = 0; // After the 1000th addr, the seeder will only add one more address from an addr // message. @@ -75,6 +79,43 @@ return payload; } +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 = merkleRoot; + } 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_version_test) { CNetAddr ip; ip.SetInternal("foo.com"); @@ -166,4 +207,49 @@ BOOST_CHECK_EQUAL(VADDR_SOFT_CAP + 1, vAddr.size()); } +BOOST_AUTO_TEST_CASE(seeder_node_headers_test_correct_chain) { + CNetAddr ip; + ip.SetInternal("foo.com"); + const CService service(ip, SERVICE_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, &vAddr); + + CDataStream headersMessage = CreateHeadersMessage(SEND_GOOD_HEADER); + + bool ret = testNode.TestProcessMessage(NetMsgType::HEADERS, headersMessage); + BOOST_CHECK_EQUAL(ret, false); + BOOST_CHECK_EQUAL(testNode.GetBan(), 0); +} + +BOOST_AUTO_TEST_CASE(seeder_node_headers_test_wrong_chain) { + CNetAddr ip; + ip.SetInternal("foo.com"); + const CService service(ip, SERVICE_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, &vAddr); + + CDataStream headersMessage = CreateHeadersMessage(SEND_WRONG_CHAIN_HEADER); + + bool ret = testNode.TestProcessMessage(NetMsgType::HEADERS, headersMessage); + BOOST_CHECK_EQUAL(ret, true); + BOOST_CHECK_EQUAL(testNode.GetBan(), 100000); +} + +BOOST_AUTO_TEST_CASE(seeder_node_empty_headers_test) { + CNetAddr ip; + ip.SetInternal("foo.com"); + const CService service(ip, SERVICE_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, &vAddr); + + CDataStream headersMessage = CreateHeadersMessage(SEND_NO_HEADERS); + + bool ret = testNode.TestProcessMessage(NetMsgType::HEADERS, headersMessage); + BOOST_CHECK_EQUAL(ret, true); + BOOST_CHECK_EQUAL(testNode.GetBan(), 100000); +} + BOOST_AUTO_TEST_SUITE_END()