mirror of
https://github.com/wownero/wownero.git
synced 2026-10-17 23:51:37 +02:00
+3









j-berman
jeffro256
Luke Parker
Lee *!* Clagett
SyntheticBird45
jeetrex17
UkoeHB
MrCyjaneK
tobtoht
selsta
Thomas
navidR
Boog900
a9145f9d38
Co-authored-by: jeffro256 <jeffro256@tutanota.com> Co-authored-by: Luke Parker <lukeparker5132@gmail.com> Co-authored-by: Lee *!* Clagett <code@leeclagett.com> Co-authored-by: SyntheticBird45 <someoneelse.is_on.github.rio7x@simplelogin.com> Co-authored-by: jeetrex17 <jeetrex@tutamail.com> Co-authored-by: UkoeHB <ukoehb@users.noreply.github.com> Co-authored-by: MrCyjaneK Co-authored-by: tobtoht Co-authored-by: selsta Co-authored-by: Thomas <thomas.giudici@proton.me> Co-authored-by: navidR Co-authored-by: Boog900 <boog900@tutanota.com>
256 lines
10 KiB
C++
256 lines
10 KiB
C++
// Copyright (c) 2014, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "cryptonote_protocol/request_manager.h"
|
|
|
|
#include <boost/uuid/uuid.hpp>
|
|
#include <unistd.h>
|
|
|
|
static boost::uuids::uuid uuid_from_char(char a)
|
|
{
|
|
boost::uuids::uuid u;
|
|
memset(&u, a, 16);
|
|
return u;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(request_manager, basic_usage)
|
|
{
|
|
const uint8_t MAX_IN_FLIGHT = 3;
|
|
request_manager req_manager(MAX_IN_FLIGHT);
|
|
|
|
const boost::uuids::uuid peer{};
|
|
|
|
// 1. Fill up the request manager
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
// Success case
|
|
ASSERT_EQ(req_manager.enqueue_requests({crypto::hash{i}}, peer).tx_hashes.size(), 1);
|
|
|
|
// Can't re-add
|
|
ASSERT_EQ(req_manager.enqueue_requests({crypto::hash{i}}, peer).tx_hashes.size(), 0);
|
|
}
|
|
|
|
// 2. Queue is full and can't let any others fly. Queue this one
|
|
ASSERT_EQ(req_manager.enqueue_requests({crypto::hash{MAX_IN_FLIGHT}}, peer).tx_hashes.size(), 0);
|
|
|
|
// 3. Remove 1 from the queue to make room for one queued above
|
|
ASSERT_TRUE(req_manager.remove_request(crypto::hash{0}));
|
|
ASSERT_FALSE(req_manager.remove_request(crypto::hash{0}));
|
|
|
|
// 4. Make sure the one queued above enters flight
|
|
const auto tx_reqs = req_manager.fly_available_requests(peer).tx_hashes;
|
|
ASSERT_EQ(tx_reqs.size(), 1);
|
|
ASSERT_EQ(tx_reqs.front(), crypto::hash{MAX_IN_FLIGHT});
|
|
|
|
// 5. Remove
|
|
ASSERT_TRUE(req_manager.remove_request(crypto::hash{1}));
|
|
ASSERT_TRUE(req_manager.remove_request(crypto::hash{2}));
|
|
ASSERT_TRUE(req_manager.remove_request(crypto::hash{MAX_IN_FLIGHT}));
|
|
req_manager.remove_peer(peer);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(request_manager, multiple_peers)
|
|
{
|
|
const uint8_t MAX_IN_FLIGHT = 4;
|
|
const int64_t TIMEOUT_MS = 10;
|
|
request_manager req_manager(MAX_IN_FLIGHT, TIMEOUT_MS);
|
|
|
|
// 1. Add 1 for each peer
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
const crypto::hash hash{i};
|
|
const boost::uuids::uuid peer = uuid_from_char(i);
|
|
|
|
// Success case
|
|
ASSERT_EQ(req_manager.enqueue_requests({hash}, peer).tx_hashes.size(), 1);
|
|
|
|
// Can't re-add
|
|
ASSERT_EQ(req_manager.enqueue_requests({hash}, peer).tx_hashes.size(), 0);
|
|
}
|
|
|
|
// 2. No stale requests and none available
|
|
{
|
|
const auto drop_peers = req_manager.remove_stale_requests();
|
|
ASSERT_TRUE(drop_peers.empty());
|
|
for (uint8_t i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
const boost::uuids::uuid peer = uuid_from_char(i);
|
|
const auto tx_hashes = req_manager.fly_available_requests(peer).tx_hashes;
|
|
ASSERT_TRUE(tx_hashes.empty());
|
|
}
|
|
}
|
|
|
|
// 3. Sleep to let requests timeout
|
|
MINFO("Sleeping for 20ms to let requests timeout");
|
|
usleep(TIMEOUT_MS * 1000 * 2);
|
|
|
|
// 4. Re-add the same ones for each peer, but in reverse. The hashes
|
|
// should get queued for each peer
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
const crypto::hash hash{i};
|
|
const boost::uuids::uuid peer = uuid_from_char(MAX_IN_FLIGHT - i - 1);
|
|
ASSERT_EQ(req_manager.enqueue_requests({hash}, peer).tx_hashes.size(), 0);
|
|
}
|
|
|
|
// 5. Remove stale requests
|
|
const auto drop_peers = req_manager.remove_stale_requests();
|
|
ASSERT_TRUE(drop_peers.empty());
|
|
|
|
// 6. Available reqs should be all the tx hashes from step 4
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
const crypto::hash hash{i};
|
|
const boost::uuids::uuid peer = uuid_from_char(MAX_IN_FLIGHT - i - 1);
|
|
const auto tx_hashes = req_manager.fly_available_requests(peer).tx_hashes;
|
|
|
|
ASSERT_EQ(tx_hashes.size(), 1);
|
|
ASSERT_EQ(tx_hashes.front(), hash);
|
|
}
|
|
|
|
// 7. Remove peers
|
|
for (uint8_t i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
{
|
|
const boost::uuids::uuid peer = uuid_from_char(i);
|
|
req_manager.remove_peer(peer);
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(request_manager, drop_peers)
|
|
{
|
|
const uint8_t MAX_IN_FLIGHT = P2P_MIN_SAMPLE_SIZE_FOR_DROPPING;
|
|
const int64_t TIMEOUT_MS = 10;
|
|
request_manager req_manager(MAX_IN_FLIGHT, TIMEOUT_MS);
|
|
|
|
const boost::uuids::uuid peer{};
|
|
|
|
// 1. Add many requests
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
ASSERT_EQ(req_manager.enqueue_requests({crypto::hash{i}}, peer).tx_hashes.size(), 1);
|
|
|
|
// 2. Sleep to let requests timeout
|
|
MINFO("Sleeping for 20ms to let requests timeout");
|
|
usleep(TIMEOUT_MS * 1000 * 2);
|
|
|
|
// 3. Remove stale requests
|
|
const auto drop_peers = req_manager.remove_stale_requests();
|
|
ASSERT_EQ(drop_peers.size(), 1);
|
|
ASSERT_EQ(drop_peers.count(peer), 1);
|
|
|
|
// 4. Remove peers
|
|
req_manager.remove_peer(peer);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(request_manager, dont_rm_processing_txs)
|
|
{
|
|
const uint8_t MAX_IN_FLIGHT = P2P_MIN_SAMPLE_SIZE_FOR_DROPPING;
|
|
const int64_t TIMEOUT_MS = 10;
|
|
request_manager req_manager(MAX_IN_FLIGHT, TIMEOUT_MS);
|
|
|
|
const boost::uuids::uuid peer{};
|
|
|
|
// 1. Add many requests
|
|
std::vector<crypto::hash> init_hashes;
|
|
init_hashes.reserve(MAX_IN_FLIGHT);
|
|
for (char i = 0; i < MAX_IN_FLIGHT; ++i)
|
|
init_hashes.emplace_back(crypto::hash{i});
|
|
const auto tx_req = req_manager.enqueue_requests(init_hashes, peer);
|
|
ASSERT_EQ(tx_req.tx_hashes, init_hashes);
|
|
ASSERT_EQ(tx_req.nonce, 1);
|
|
|
|
// 2. Indicate that we're processing all of the tx requests
|
|
// Note: the last one is explicitly not included in the processing_hashes, which technically is supposed to mean
|
|
// the peer didn't have the tx and it can be removed from the request queue.
|
|
const std::vector<crypto::hash> processing_hashes(init_hashes.begin(), init_hashes.end() - 1);
|
|
ASSERT_TRUE(req_manager.processing_txs(peer, tx_req.nonce, processing_hashes));
|
|
ASSERT_FALSE(req_manager.remove_request(init_hashes.back()));
|
|
|
|
// 3. Sleep to let requests timeout
|
|
MINFO("Sleeping for 20ms to let requests timeout");
|
|
usleep(TIMEOUT_MS * 1000 * 2);
|
|
|
|
// 4. There should be no stale requests.
|
|
const auto drop_peers = req_manager.remove_stale_requests();
|
|
ASSERT_EQ(drop_peers.size(), 0);
|
|
|
|
// 5. Remove peers
|
|
req_manager.remove_peer(peer);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
TEST(request_manager, dont_rm_processing_txs_enqueue_overage)
|
|
{
|
|
const uint8_t MAX_IN_FLIGHT = 2;
|
|
const int64_t TIMEOUT_MS = 10;
|
|
request_manager req_manager(MAX_IN_FLIGHT, TIMEOUT_MS);
|
|
|
|
const boost::uuids::uuid peer{};
|
|
|
|
// 1. Enqueue 1 more request than max allowed in flight
|
|
const uint8_t TOTAL_TXS = MAX_IN_FLIGHT + 1;
|
|
std::vector<crypto::hash> init_hashes;
|
|
init_hashes.reserve(TOTAL_TXS);
|
|
for (char i = 0; i < TOTAL_TXS; ++i)
|
|
init_hashes.emplace_back(crypto::hash{i});
|
|
const auto tx_req = req_manager.enqueue_requests(init_hashes, peer);
|
|
ASSERT_EQ(tx_req.tx_hashes, std::vector<crypto::hash>(init_hashes.begin(), init_hashes.begin() + MAX_IN_FLIGHT));
|
|
ASSERT_EQ(tx_req.nonce, 1);
|
|
|
|
// 2. Remove 1 req to make room for the last one
|
|
ASSERT_TRUE(req_manager.remove_request(init_hashes.front()));
|
|
|
|
// 3. Now fly the last one
|
|
const auto tx_req2 = req_manager.fly_available_requests(peer);
|
|
ASSERT_EQ(tx_req2.tx_hashes, std::vector<crypto::hash>{init_hashes.back()});
|
|
ASSERT_EQ(tx_req2.nonce, 2);
|
|
|
|
// 4. Indicate that we're processing Step 3's tx
|
|
ASSERT_FALSE(req_manager.processing_txs(peer, tx_req2.nonce, tx_req2.tx_hashes));
|
|
|
|
// 5. Sleep to let requests timeout
|
|
MINFO("Sleeping for 20ms to let requests timeout");
|
|
usleep(TIMEOUT_MS * 1000 * 2);
|
|
|
|
// 6. The processing tx from step 4 should still be present, since it's processing and shouldn't have been rm'd.
|
|
// init_hashes.at(1) should have timed out and rm'd. Since this is only the first stale tx req rm'd, the peer
|
|
// is not expected to drop.
|
|
static_assert(P2P_MIN_SAMPLE_SIZE_FOR_DROPPING > 1);
|
|
const auto drop_peers = req_manager.remove_stale_requests();
|
|
ASSERT_EQ(drop_peers.size(), 0);
|
|
ASSERT_FALSE(req_manager.remove_request(init_hashes.at(1)));
|
|
ASSERT_TRUE(req_manager.remove_request(init_hashes.back()));
|
|
|
|
// 7. Remove peer
|
|
req_manager.remove_peer(peer);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------------------------------------------------
|