diff --git a/src/Makefile.seedertest.include b/src/Makefile.seedertest.include --- a/src/Makefile.seedertest.include +++ b/src/Makefile.seedertest.include @@ -12,7 +12,10 @@ seeder/test/seeder_tests.cpp seeder_test_test_bitcoin_seeder_LDADD = $(LIBBITCOIN_SEEDER) -seeder_test_test_bitcoin_seeder_LDADD += $(LIBUNIVALUE) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + +seeder_test_test_bitcoin_seeder_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \ + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(CRYPTO_LIBS) + seeder_test_test_bitcoin_seeder_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) seeder_test_test_bitcoin_seeder_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/seeder/CMakeLists.txt b/src/seeder/CMakeLists.txt --- a/src/seeder/CMakeLists.txt +++ b/src/seeder/CMakeLists.txt @@ -11,11 +11,9 @@ ) target_link_libraries(seeder-net - common + bitcoinconsensus ) -target_include_directories(seeder-net PUBLIC .) - add_executable(bitcoin-seeder main.cpp) target_link_libraries(bitcoin-seeder seeder-net) diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h --- a/src/seeder/bitcoin.h +++ b/src/seeder/bitcoin.h @@ -53,10 +53,11 @@ void GotVersion(); - bool ProcessMessage(std::string strCommand, CDataStream &recv); - bool ProcessMessages(); +protected: + bool ProcessMessage(std::string strCommand, CDataStream &recv); + public: CSeederNode(const CService &ip, std::vector *vAddrIn); 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,15 +4,29 @@ #define BOOST_TEST_MODULE Bitcoin Seeder Test Suite +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include +#include #include #include #include #include +#include + +const std::function G_TRANSLATION_FUN = nullptr; BOOST_AUTO_TEST_SUITE(seeder) @@ -24,6 +38,12 @@ static const uint16_t QUESTION_TYPE = 1; static const uint8_t END_OF_NAME_FIELD = 0; static const size_t MAX_LABEL_LENGTH = 63; +static const unsigned short MAINNET_PORT = 8333; +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 SEED = "seed.bitcoinabc.org"; +static const uint32_t VERACK_PAYLOAD = 0; // Builds dummy DNS query message std::array @@ -66,6 +86,89 @@ return messageBuffer; } +class TestCSeederNode : public CSeederNode { +public: + TestCSeederNode(const CService &service, std::vector &vAddrIn) + : CSeederNode(service, &vAddr), vAddr(vAddrIn), + nVersion(INIT_PROTO_VERSION), message(SER_NETWORK, 0), + header(netMagic, ADDR_COMMAND.c_str(), 0) {} + + bool TestProcessMessage(const std::string &strCommand) { + if (strCommand == VERSION_COMMAND) { + CreateVersionMessage(); + return CSeederNode::ProcessMessage(VERSION_COMMAND, message); + } else if (strCommand == VERACK_COMMAND) { + CreateVerackMessage(); + return CSeederNode::ProcessMessage(VERACK_COMMAND, message); + } else { + // strCommand == ADDR_COMMAND + CreateAddrMessage(); + return CSeederNode::ProcessMessage(ADDR_COMMAND, message); + } + } + + void setVersion(int32_t version) { nVersion = version; } + + size_t getAddrListSize() { return vAddr.size(); } + +private: + void CreateVersionMessage() { + CDataStream payload(SER_NETWORK, 0); + payload.SetVersion(nVersion); + ServiceFlags service = ServiceFlags(NODE_NETWORK); + int64_t now = time(nullptr); + uint64_t nonce = 0; + uint16_t user_agent = 0; + CAddress addrTo = vAddr[0]; + CService serviceFrom; + CAddress addrFrom(serviceFrom, + ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)); + int32_t start_height = GetRequireHeight(false); + payload << nVersion << static_cast(service) << now << addrTo + << addrFrom << nonce << user_agent << start_height; + message.erase(message.begin(), message.end()); + message.SetVersion(nVersion); + message += payload; + + header.nMessageSize = payload.size(); + uint256 hash = Hash(payload.begin(), payload.end()); + memcpy(header.pchChecksum, &hash, header.CHECKSUM_SIZE); + } + + // Verack message consists of only the header with VERACK_COMMAND as the + // command string. + void CreateVerackMessage() { + header.nMessageSize = 0; + CDataStream payload(SER_NETWORK, 0); + payload.SetVersion(nVersion); + payload << VERACK_PAYLOAD; + uint256 hash = Hash(payload.begin(), payload.end()); + memcpy(header.pchChecksum, &hash, header.CHECKSUM_SIZE); + + message.erase(message.begin(), message.end()); + message.SetVersion(nVersion); + message << header.pchMessageStart << header.GetCommand() + << header.nMessageSize << header.pchChecksum; + } + + void CreateAddrMessage() { + CDataStream payload(SER_NETWORK, 0); + payload.SetVersion(nVersion); + payload << vAddr; + message.erase(message.begin(), message.end()); + message += payload; + + header.nMessageSize = payload.size(); + uint256 hash = Hash(payload.begin(), payload.end()); + memcpy(header.pchChecksum, &hash, header.CHECKSUM_SIZE); + } + + std::vector vAddr; + int32_t nVersion; + CDataStream message; + CMessageHeader header; +}; + BOOST_AUTO_TEST_CASE(parse_name_happy_path) { const std::string messageQualifierName = "www.mydomain.com"; std::array dnsMessage = @@ -145,4 +248,95 @@ BOOST_CHECK_EQUAL(ret, -1); } +BOOST_AUTO_TEST_CASE(seeder_node_default_version_tests) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(999, addr); + TestCSeederNode testNode(service, vAddr); + + bool ret = testNode.TestProcessMessage(VERSION_COMMAND); + BOOST_CHECK_EQUAL(ret, false); +} + +BOOST_AUTO_TEST_CASE(seeder_node_version_10300_tests) { + 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); + testNode.setVersion(10300); + + bool ret = testNode.TestProcessMessage(VERSION_COMMAND); + BOOST_CHECK_EQUAL(ret, false); +} + +BOOST_AUTO_TEST_CASE(seeder_node_version_106_tests) { + 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); + testNode.setVersion(106); + + bool ret = testNode.TestProcessMessage(VERSION_COMMAND); + BOOST_CHECK_EQUAL(ret, false); +} + +BOOST_AUTO_TEST_CASE(seeder_node_verack_tests) { + 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); + + bool ret = testNode.TestProcessMessage(VERACK_COMMAND); + BOOST_CHECK_EQUAL(ret, false); +} + +BOOST_AUTO_TEST_CASE(seeder_node_addr_test_happy_path) { + 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); + + bool ret = testNode.TestProcessMessage(ADDR_COMMAND); + BOOST_CHECK_EQUAL(ret, false); + BOOST_CHECK_EQUAL(2, testNode.getAddrListSize()); +} + +BOOST_AUTO_TEST_CASE(seeder_node_addr_test_until_full) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(500, addr); + TestCSeederNode testNode(service, vAddr); + + // Maximum number of addrs is 1000. All addrs are added until the 1000th. + bool ret = testNode.TestProcessMessage(ADDR_COMMAND); + BOOST_CHECK_EQUAL(ret, false); + BOOST_CHECK_EQUAL(1000, testNode.getAddrListSize()); +} + +BOOST_AUTO_TEST_CASE(seeder_node_addr_test_full_vaddr) { + CNetAddr ip; + LookupHost(SEED.c_str(), ip, true); + const CService service(ip, MAINNET_PORT); + CAddress addr(service, ServiceFlags()); + std::vector vAddr(1000, addr); + TestCSeederNode testNode(service, vAddr); + + // After the 1000th, only one is added and the rest are ignored + bool ret = testNode.TestProcessMessage(ADDR_COMMAND); + BOOST_CHECK_EQUAL(ret, true); + BOOST_CHECK_EQUAL(1001, testNode.getAddrListSize()); +} + BOOST_AUTO_TEST_SUITE_END()