Changeset View
Changeset View
Standalone View
Standalone View
src/seeder/test/seeder_tests.cpp
// Copyright (c) 2019 The Bitcoin developers | // Copyright (c) 2019 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#define BOOST_TEST_MODULE Bitcoin Seeder Test Suite | #define BOOST_TEST_MODULE Bitcoin Seeder Test Suite | ||||
#include <hash.h> | |||||
#include <netbase.h> | |||||
#include <protocol.h> | |||||
#include <seeder/bitcoin.h> | |||||
#include <seeder/db.h> | |||||
#include <seeder/dns.h> | #include <seeder/dns.h> | ||||
#include <serialize.h> | |||||
#include <streams.h> | |||||
#include <uint256.h> | |||||
#include <util/system.h> | |||||
#include <version.h> | |||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
#include <array> | #include <array> | ||||
#include <functional> | |||||
#include <iomanip> | #include <iomanip> | ||||
#include <iostream> | #include <iostream> | ||||
#include <sstream> | #include <sstream> | ||||
#include <string> | #include <string> | ||||
#include <vector> | |||||
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr; | |||||
BOOST_AUTO_TEST_SUITE(seeder) | BOOST_AUTO_TEST_SUITE(seeder) | ||||
static const int BUFFER_LENGTH = 512; | static const int BUFFER_LENGTH = 512; | ||||
static const int QUALIFIER_NAME_BUFFER_LENGTH = 256; | static const int QUALIFIER_NAME_BUFFER_LENGTH = 256; | ||||
static const int SIZE_OF_QUESTION_TYPE = 2; | static const int SIZE_OF_QUESTION_TYPE = 2; | ||||
static const int SIZE_OF_QUESTION_CLASS = 2; | static const int SIZE_OF_QUESTION_CLASS = 2; | ||||
static const uint16_t QUESTION_CLASS = 1; | static const uint16_t QUESTION_CLASS = 1; | ||||
static const uint16_t QUESTION_TYPE = 1; | static const uint16_t QUESTION_TYPE = 1; | ||||
static const uint8_t END_OF_NAME_FIELD = 0; | static const uint8_t END_OF_NAME_FIELD = 0; | ||||
static const size_t MAX_LABEL_LENGTH = 63; | 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 | // Builds dummy DNS query message | ||||
std::array<uint8_t, BUFFER_LENGTH> | std::array<uint8_t, BUFFER_LENGTH> | ||||
CreateDNSQuestion(const std::string &qualifierName) { | CreateDNSQuestion(const std::string &qualifierName) { | ||||
std::stringstream querryhex; | std::stringstream querryhex; | ||||
querryhex.clear(); | querryhex.clear(); | ||||
querryhex << std::hex << std::setfill('0'); | querryhex << std::hex << std::setfill('0'); | ||||
Show All 26 Lines | CreateDNSQuestion(const std::string &qualifierName) { | ||||
querryhex << QUESTION_CLASS; | querryhex << QUESTION_CLASS; | ||||
std::array<uint8_t, BUFFER_LENGTH> messageBuffer; | std::array<uint8_t, BUFFER_LENGTH> messageBuffer; | ||||
messageBuffer.fill(0); | messageBuffer.fill(0); | ||||
querryhex >> std::hex >> messageBuffer.data(); | querryhex >> std::hex >> messageBuffer.data(); | ||||
return messageBuffer; | return messageBuffer; | ||||
} | } | ||||
class TestCSeederNode : public CSeederNode { | |||||
public: | |||||
TestCSeederNode(const CService &service, std::vector<CAddress> &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 << uint64_t(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<CAddress> vAddr; | |||||
int32_t nVersion; | |||||
CDataStream message; | |||||
CMessageHeader header; | |||||
jasonbcox: This looks like a bug in the making since forgetting to reset these can produce unexpected… | |||||
}; | |||||
BOOST_AUTO_TEST_CASE(parse_name_happy_path) { | BOOST_AUTO_TEST_CASE(parse_name_happy_path) { | ||||
const std::string messageQualifierName = "www.mydomain.com"; | const std::string messageQualifierName = "www.mydomain.com"; | ||||
std::array<uint8_t, BUFFER_LENGTH> dnsMessage = | std::array<uint8_t, BUFFER_LENGTH> dnsMessage = | ||||
CreateDNSQuestion(messageQualifierName); | CreateDNSQuestion(messageQualifierName); | ||||
std::array<char, QUALIFIER_NAME_BUFFER_LENGTH> qualifierName; | std::array<char, QUALIFIER_NAME_BUFFER_LENGTH> qualifierName; | ||||
qualifierName.fill(0); | qualifierName.fill(0); | ||||
size_t writeBufferSize = QUALIFIER_NAME_BUFFER_LENGTH; | size_t writeBufferSize = QUALIFIER_NAME_BUFFER_LENGTH; | ||||
const uint8_t *messageBegin = dnsMessage.data(); | const uint8_t *messageBegin = dnsMessage.data(); | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(parse_name_field_name_too_long) { | ||||
const uint8_t *messageBegin = dnsMessage.data(); | const uint8_t *messageBegin = dnsMessage.data(); | ||||
const uint8_t *messageEnd = dnsMessage.data() + tooLongQName.size() + 1 + | const uint8_t *messageEnd = dnsMessage.data() + tooLongQName.size() + 1 + | ||||
SIZE_OF_QUESTION_TYPE + SIZE_OF_QUESTION_CLASS; | SIZE_OF_QUESTION_TYPE + SIZE_OF_QUESTION_CLASS; | ||||
int ret = parse_name(&messageBegin, messageEnd, dnsMessage.data(), | int ret = parse_name(&messageBegin, messageEnd, dnsMessage.data(), | ||||
qualifierName.data(), writeBufferSize); | qualifierName.data(), writeBufferSize); | ||||
BOOST_CHECK_EQUAL(ret, -1); | 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<CAddress> 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) { | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsI don't know what version 10300 is or why it's useful to test. Please give it a useful name. jasonbcox: I don't know what version 10300 is or why it's useful to test. Please give it a useful name. | |||||
nakihitoAuthorUnsubmitted Done Inline ActionsTurns out the path this is testing can be removed after D4449 is landed. nakihito: Turns out the path this is testing can be removed after D4449 is landed. | |||||
CNetAddr ip; | |||||
LookupHost(SEED.c_str(), ip, true); | |||||
const CService service(ip, MAINNET_PORT); | |||||
CAddress addr(service, ServiceFlags()); | |||||
std::vector<CAddress> 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) { | |||||
jasonbcoxUnsubmitted Not Done Inline Actionsditto jasonbcox: ditto | |||||
CNetAddr ip; | |||||
LookupHost(SEED.c_str(), ip, true); | |||||
const CService service(ip, MAINNET_PORT); | |||||
CAddress addr(service, ServiceFlags()); | |||||
std::vector<CAddress> 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<CAddress> 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<CAddress> 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<CAddress> 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); | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsThis test is hard to follow because the seeder is sending a message to itself with the vAddr list that it already has. This "works" but the intent is impossible to follow. The test should explicitly send some number of addresses to the seeder and test against that expectation. Otherwise you look at this test with no way of knowing what the right behavior should be. jasonbcox: This test is hard to follow because the seeder is sending a message to itself with the vAddr… | |||||
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<CAddress> 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); | |||||
jasonbcoxUnsubmitted Not Done Inline Actionsditto jasonbcox: ditto | |||||
BOOST_CHECK_EQUAL(ret, true); | |||||
BOOST_CHECK_EQUAL(1001, testNode.getAddrListSize()); | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |
This looks like a bug in the making since forgetting to reset these can produce unexpected results. message and header are only used on a per-message basis, so why are they class members?