#include <banman.h>
#include <chainparams.h>
#include <common/args.h>
#include <net.h>
#include <net_processing.h>
#include <pubkey.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <serialize.h>
#include <test/util/net.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <util/string.h>
#include <util/time.h>
#include <validation.h>
#include <array>
#include <cstdint>
#include <boost/test/unit_test.hpp>
static CService ip(uint32_t i)
{
struct in_addr s;
s.s_addr = i;
return CService(CNetAddr(s), Params().GetDefaultPort());
}
BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
LOCK(NetEventsInterface::g_msgproc_mutex);
ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
connman.SetPeerConnectTimeout(99999s);
PeerManager& peerman = *m_node.peerman;
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
NodeId id{0};
CNode dummyNode1{id++,
nullptr,
addr1,
0,
0,
CAddress(),
"",
ConnectionType::OUTBOUND_FULL_RELAY,
false,
0};
connman.Handshake(
dummyNode1,
true,
ServiceFlags(NODE_NETWORK | NODE_WITNESS),
ServiceFlags(NODE_NETWORK | NODE_WITNESS),
PROTOCOL_VERSION,
true);
{
LOCK(cs_main);
BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
}
BOOST_CHECK(peerman.SendMessages(dummyNode1));
{
LOCK(dummyNode1.cs_vSend);
const auto& [to_send, _more, _msg_type] = dummyNode1.m_transport->GetBytesToSend(false);
BOOST_CHECK(!to_send.empty());
}
connman.FlushSendBuffer(dummyNode1);
NodeClockContext clock_ctx{};
clock_ctx += 21min;
BOOST_CHECK(peerman.SendMessages(dummyNode1)); {
LOCK(dummyNode1.cs_vSend);
const auto& [to_send, _more, _msg_type] = dummyNode1.m_transport->GetBytesToSend(false);
BOOST_CHECK(!to_send.empty());
}
clock_ctx += 3min;
BOOST_CHECK(peerman.SendMessages(dummyNode1)); BOOST_CHECK(dummyNode1.fDisconnect == true);
peerman.FinalizeNode(dummyNode1);
}
struct OutboundTest : TestingSetup {
void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman, ConnectionType connType, bool onion_peer = false)
{
CAddress addr;
if (onion_peer) {
auto tor_addr{m_rng.randbytes(ADDR_TORV3_SIZE)};
BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr)));
}
while (!addr.IsRoutable()) {
addr = CAddress(ip(m_rng.randbits(32)), NODE_NONE);
}
vNodes.emplace_back(new CNode{id++,
nullptr,
addr,
0,
0,
CAddress(),
"",
connType,
false,
0});
CNode &node = *vNodes.back();
node.SetCommonVersion(PROTOCOL_VERSION);
peerLogic.InitializeNode(node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));
node.fSuccessfullyConnected = true;
connman.AddTestNode(node);
}
};
BOOST_FIXTURE_TEST_CASE(stale_tip_peer_management, OutboundTest)
{
NodeId id{0};
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params());
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, *m_node.warnings, {});
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
CConnman::Options options;
options.m_max_automatic_connections = DEFAULT_MAX_PEER_CONNECTIONS;
const auto time_init{Now<NodeSeconds>()};
NodeClockContext clock_ctx{time_init};
const auto delta{3 * std::chrono::seconds{m_node.chainman->GetConsensus().nPowTargetSpacing} + 1s};
connman->Init(options);
std::vector<CNode *> vNodes;
for (int i = 0; i < max_outbound_full_relay; ++i) {
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
}
peerLogic->CheckForStaleTipAndEvictPeers();
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}
clock_ctx += delta;
peerLogic->CheckForStaleTipAndEvictPeers();
BOOST_CHECK(connman->GetTryNewOutboundPeer());
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}
clock_ctx.set(time_init);
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
clock_ctx += delta;
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes.back()->fDisconnect == true);
vNodes.back()->fDisconnect = false;
peerLogic->UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay - 1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
BOOST_CHECK(vNodes.back()->fDisconnect == false);
vNodes[max_outbound_full_relay - 1]->fDisconnect = false;
clock_ctx.set(time_init);
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY, true);
clock_ctx += delta;
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay - 2; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[max_outbound_full_relay - 2]->fDisconnect == false);
BOOST_CHECK(vNodes[max_outbound_full_relay - 1]->fDisconnect == true);
BOOST_CHECK(vNodes[max_outbound_full_relay]->fDisconnect == false);
clock_ctx.set(time_init);
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY, true);
clock_ctx += delta;
peerLogic->CheckForStaleTipAndEvictPeers();
BOOST_CHECK(vNodes.back()->fDisconnect == true);
for (const CNode *node : vNodes) {
peerLogic->FinalizeNode(*node);
}
connman->ClearTestNodes();
}
BOOST_FIXTURE_TEST_CASE(block_relay_only_eviction, OutboundTest)
{
NodeId id{0};
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params());
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, *m_node.warnings, {});
constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS};
constexpr auto MINIMUM_CONNECT_TIME{30s};
CConnman::Options options;
options.m_max_automatic_connections = DEFAULT_MAX_PEER_CONNECTIONS;
connman->Init(options);
std::vector<CNode*> vNodes;
for (int i = 0; i < max_outbound_block_relay; ++i) {
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::BLOCK_RELAY);
}
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_block_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::BLOCK_RELAY);
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_block_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes.back()->fDisconnect == false);
NodeClockContext clock_ctx{};
clock_ctx += MINIMUM_CONNECT_TIME;
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_block_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes.back()->fDisconnect == true);
vNodes.back()->fDisconnect = false;
vNodes.back()->m_last_block_time = GetTime<std::chrono::seconds>();
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_block_relay - 1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[max_outbound_block_relay - 1]->fDisconnect == true);
BOOST_CHECK(vNodes.back()->fDisconnect == false);
for (const CNode* node : vNodes) {
peerLogic->FinalizeNode(*node);
}
connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
LOCK(NetEventsInterface::g_msgproc_mutex);
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params());
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), *m_node.chainman, *m_node.mempool, *m_node.warnings, {});
CNetAddr tor_netaddr;
BOOST_REQUIRE(
tor_netaddr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
const CService tor_service{tor_netaddr, Params().GetDefaultPort()};
const std::array<CAddress, 3> addr{CAddress{ip(0xa0b0c001), NODE_NONE},
CAddress{ip(0xa0b0c002), NODE_NONE},
CAddress{tor_service, NODE_NONE}};
const CNetAddr other_addr{ip(0xa0b0ff01)};
std::array<CNode*, 3> nodes;
banman->ClearBanned();
NodeId id{0};
nodes[0] = new CNode{id++,
nullptr,
addr[0],
0,
0,
CAddress(),
"",
ConnectionType::INBOUND,
false,
1};
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
nodes[0]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[0]);
peerLogic->UnitTestMisbehaving(nodes[0]->GetId()); BOOST_CHECK(peerLogic->SendMessages(*nodes[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(!banman->IsDiscouraged(other_addr));
nodes[1] = new CNode{id++,
nullptr,
addr[1],
1,
1,
CAddress(),
"",
ConnectionType::INBOUND,
false,
1};
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
nodes[1]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[1]);
BOOST_CHECK(peerLogic->SendMessages(*nodes[1]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
BOOST_CHECK(!nodes[1]->fDisconnect);
peerLogic->UnitTestMisbehaving(nodes[1]->GetId());
BOOST_CHECK(peerLogic->SendMessages(*nodes[1]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
BOOST_CHECK(nodes[1]->fDisconnect);
nodes[2] = new CNode{id++,
nullptr,
addr[2],
1,
1,
CAddress(),
"",
ConnectionType::OUTBOUND_FULL_RELAY,
false,
2};
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
nodes[2]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[2]);
peerLogic->UnitTestMisbehaving(nodes[2]->GetId());
BOOST_CHECK(peerLogic->SendMessages(*nodes[2]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
BOOST_CHECK(banman->IsDiscouraged(addr[2]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(nodes[1]->fDisconnect);
BOOST_CHECK(nodes[2]->fDisconnect);
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node);
}
connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
LOCK(NetEventsInterface::g_msgproc_mutex);
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params());
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), *m_node.chainman, *m_node.mempool, *m_node.warnings, {});
banman->ClearBanned();
const NodeClockContext clock_ctx{};
CAddress addr(ip(0xa0b0c001), NODE_NONE);
NodeId id{0};
CNode dummyNode{id++,
nullptr,
addr,
4,
4,
CAddress(),
"",
ConnectionType::INBOUND,
false,
1};
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
dummyNode.fSuccessfullyConnected = true;
peerLogic->UnitTestMisbehaving(dummyNode.GetId());
BOOST_CHECK(peerLogic->SendMessages(dummyNode));
BOOST_CHECK(banman->IsDiscouraged(addr));
peerLogic->FinalizeNode(dummyNode);
}
BOOST_AUTO_TEST_SUITE_END()