diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -341,6 +341,7 @@ netaddress.cpp netbase.cpp primitives/block.cpp + primitives/transaction.cpp protocol.cpp scheduler.cpp warnings.cpp @@ -367,7 +368,6 @@ add_library(bitcoinconsensus arith_uint256.cpp hash.cpp - primitives/transaction.cpp pubkey.cpp uint256.cpp util/strencodings.cpp diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -558,7 +558,8 @@ $(LIBBITCOIN_SEEDER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ - $(LIBBITCOIN_CRYPTO) + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CONSENSUS) bitcoin_seeder_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h --- a/src/seeder/bitcoin.h +++ b/src/seeder/bitcoin.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_SEEDER_BITCOIN_H #define BITCOIN_SEEDER_BITCOIN_H +#include #include #include @@ -58,6 +59,8 @@ protected: bool ProcessMessage(std::string strCommand, CDataStream &recv); + std::unique_ptr networkParams; + public: CSeederNode(const CService &ip, std::vector *vAddrIn); 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 @@ -165,6 +167,43 @@ return false; } + if (strCommand == NetMsgType::HEADERS) { + unsigned int nHeaders = ReadCompactSize(recv); + if (nHeaders == 0) { + // fprintf(stderr, + //"Peer (%s) did not find seeder checkpoint, bannded.\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 (networkParams->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; } @@ -230,11 +269,15 @@ CSeederNode::CSeederNode(const CService &ip, std::vector *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 | NODE_BITCOIN_CASH)) { + doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)), + networkParams(nullptr) { if (time(nullptr) > 1329696000) { vSend.SetVersion(209); vRecv.SetVersion(209); } + std::string network = + fTestNet ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN; + networkParams = CreateChainParams(network); } bool CSeederNode::Run() { 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 @@ -4,6 +4,7 @@ #define BOOST_TEST_MODULE Bitcoin Seeder Test Suite +#include #include #include #include @@ -45,6 +46,12 @@ static const std::string VERACK_COMMAND = "verack"; static const std::string VERSION_COMMAND = "version"; static const std::string ADDR_COMMAND = "addr"; +static const std::string SENDHEADERS_COMMAND = "sendheaders"; +static const std::string HEADERS_COMMAND = "headers"; +static const int SEND_NO_HEADERS = -1; +static const int SEND_WRONG_CHAIN_HEADER = 0; +static const int SEND_GOOD_HEADER = 1; +static const uint8_t HEADERS_TXN_COUNT = 0; static const std::string SEED = "seed.bitcoinabc.org"; static const uint32_t VERACK_PAYLOAD = 0; // After the 1000th addr, the seeder will only add one more address from an addr @@ -92,9 +99,10 @@ class TestCSeederNode : public CSeederNode { public: TestCSeederNode(const CService &service, std::vector &vAddrIn, - int nAddrsToSendIn = 0) + int nAddrsToSendIn = 0, int headersTest = 1) : CSeederNode(service, &vAddr), vAddr(vAddrIn), - nAddrsToSend(nAddrsToSendIn), nVersion(INIT_PROTO_VERSION) {} + nAddrsToSend(nAddrsToSendIn), nVersion(INIT_PROTO_VERSION), + headersMessageContent(headersTest) {} bool TestProcessMessage(const std::string &strCommand) { if (strCommand == VERSION_COMMAND) { @@ -103,9 +111,13 @@ } else if (strCommand == VERACK_COMMAND) { CDataStream message = CreateVerackMessage(); return CSeederNode::ProcessMessage(VERACK_COMMAND, message); - } else { + } else if (strCommand == ADDR_COMMAND) { CDataStream message = CreateAddrMessage(); return CSeederNode::ProcessMessage(ADDR_COMMAND, message); + } else { + // strCommand == HEADERS_COMMAND + CDataStream message = CreateHeadersMessage(); + return CSeederNode::ProcessMessage(HEADERS_COMMAND, message); } } @@ -149,9 +161,46 @@ return payload; } + CDataStream CreateHeadersMessage() { + CDataStream payload(SER_NETWORK, 0); + payload.SetVersion(nVersion); + uint8_t numberOfHeaders = + headersMessageContent == SEND_NO_HEADERS ? 0 : 1; + payload << numberOfHeaders; + // The actual headers can be garbage data, Seeder only cares that it got + // a specific type of message and the size. + uint256 garbageHash = Hash(payload.begin(), payload.end()); + uint256 previousBlockHash; + if (headersMessageContent == SEND_WRONG_CHAIN_HEADER) { + previousBlockHash = garbageHash; + } else { + previousBlockHash = CSeederNode::networkParams->Checkpoints() + .mapCheckpoints.rbegin() + ->second; + } + uint32_t garbageTimeStamp = 0; + uint32_t garbageBits = 0; + uint32_t garbageNonce = 0; + payload << nVersion; + // prev_block + payload << previousBlockHash; + // merkle_root + payload << garbageHash; + // timestamp + payload << garbageTimeStamp; + // bits + payload << garbageBits; + // nonce + payload << garbageNonce; + // txn_count + payload << HEADERS_TXN_COUNT; + return payload; + } + std::vector vAddr; int nAddrsToSend; int32_t nVersion; + int headersMessageContent; }; BOOST_AUTO_TEST_CASE(parse_name_happy_path) { @@ -297,4 +346,43 @@ BOOST_CHECK_EQUAL(VADDR_SOFT_CAP + 1, testNode.getAddrListSize()); } +BOOST_AUTO_TEST_CASE(seeder_node_headers_test_correct_chain) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, vAddr, 0, SEND_GOOD_HEADER); + + bool ret = testNode.TestProcessMessage(HEADERS_COMMAND); + BOOST_CHECK_EQUAL(ret, false); + BOOST_CHECK_EQUAL(testNode.GetBan(), 0); +} + +BOOST_AUTO_TEST_CASE(seeder_node_headers_test_wrong_chain) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, vAddr, 0, SEND_WRONG_CHAIN_HEADER); + + bool ret = testNode.TestProcessMessage(HEADERS_COMMAND); + BOOST_CHECK_EQUAL(ret, true); + BOOST_CHECK_EQUAL(testNode.GetBan(), 100000); +} + +BOOST_AUTO_TEST_CASE(seeder_node_empty_headers_test) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1, addr); + TestCSeederNode testNode(service, vAddr, 0, SEND_NO_HEADERS); + + bool ret = testNode.TestProcessMessage(HEADERS_COMMAND); + BOOST_CHECK_EQUAL(ret, true); + BOOST_CHECK_EQUAL(testNode.GetBan(), 100000); +} + BOOST_AUTO_TEST_SUITE_END()