Changeset View
Changeset View
Standalone View
Standalone View
src/test/net_tests.cpp
Show First 20 Lines • Show All 975 Lines • ▼ Show 20 Lines | for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) { | ||||
[number_of_nodes](NodeEvictionCandidate &candidate) { | [number_of_nodes](NodeEvictionCandidate &candidate) { | ||||
candidate.availabilityScore = | candidate.availabilityScore = | ||||
double(number_of_nodes - candidate.id); | double(number_of_nodes - candidate.id); | ||||
candidate.peerid = PeerId(candidate.id / 42); | candidate.peerid = PeerId(candidate.id / 42); | ||||
}, | }, | ||||
protectedNodes, random_context)); | protectedNodes, random_context)); | ||||
} | } | ||||
// Nodes with a negative avalanche availability score should not be | |||||
// protected. | |||||
// In the test below nodes with an odd id number have a negative | |||||
// score, and others have a positive one, so this test is only | |||||
// guaranteed to work with a number of nodes <= 2 * 128. | |||||
if (number_of_nodes <= 256) { | |||||
std::vector<NodeEvictionCandidate> candidates = | |||||
GetRandomNodeEvictionCandidates(number_of_nodes, | |||||
random_context); | |||||
for (NodeEvictionCandidate &candidate : candidates) { | |||||
candidate.availabilityScore = | |||||
candidate.id % 2 == 0 ? 100. : -1.; | |||||
candidate.peerid = PeerId(); | |||||
} | |||||
const std::optional<NodeId> evicted_node_id = | |||||
SelectNodeToEvict(std::move(candidates)); | |||||
if (evicted_node_id) { | |||||
BOOST_CHECK_EQUAL(*evicted_node_id % 2, 1); | |||||
} | |||||
} | |||||
// An eviction is expected given >= 165 random eviction candidates. | // An eviction is expected given >= 165 random eviction candidates. | ||||
// The eviction logic protects at most four peers by net group, | // The eviction logic protects at most four peers by net group, | ||||
// eight by lowest ping time, four by last time of novel tx, four by | // eight by lowest ping time, four by last time of novel tx, four by | ||||
// last time of novel proof, up to eight non-tx-relay peers by last | // last time of novel proof, up to eight non-tx-relay peers by last | ||||
// novel block time, four by last novel block time, 128 more by | // novel block time, four by last novel block time, 128 more by | ||||
// avalanche availability score and the best node for each of 4 | // avalanche availability score and the best node for each of 4 | ||||
// avalanche peer. | // avalanche peer. | ||||
if (number_of_nodes >= 165) { | if (number_of_nodes >= 165) { | ||||
Show All 30 Lines | BOOST_AUTO_TEST_CASE(avalanche_statistics) { | ||||
CNode::AvalancheState avastats; | CNode::AvalancheState avastats; | ||||
double previousScore = avastats.getAvailabilityScore(); | double previousScore = avastats.getAvailabilityScore(); | ||||
BOOST_CHECK_SMALL(previousScore, 1e-6); | BOOST_CHECK_SMALL(previousScore, 1e-6); | ||||
// Check the statistics follow an exponential response for 1 to 10 tau | // Check the statistics follow an exponential response for 1 to 10 tau | ||||
for (size_t i = 1; i <= 10; i++) { | for (size_t i = 1; i <= 10; i++) { | ||||
for (uint32_t j = 0; j < tau; j += step) { | for (uint32_t j = 0; j < tau; j += step) { | ||||
avastats.invsPolled(1); | // target score = 2 * votes - polls - 1 | ||||
// => target score = 1 with votes = polls = 2 | |||||
avastats.invsPolled(2); | |||||
// Always respond to everything correctly | // Always respond to everything correctly | ||||
avastats.invsVoted(1); | avastats.invsVoted(2); | ||||
avastats.updateAvailabilityScore(); | avastats.updateAvailabilityScore(); | ||||
// Expect a monotonic rise | // Expect a monotonic rise | ||||
double currentScore = avastats.getAvailabilityScore(); | double currentScore = avastats.getAvailabilityScore(); | ||||
BOOST_CHECK_GE(currentScore, previousScore); | BOOST_CHECK_GE(currentScore, previousScore); | ||||
previousScore = currentScore; | previousScore = currentScore; | ||||
} | } | ||||
// We expect (1 - e^-i) after i * tau. The tolerance is expressed | // We expect (1 - e^-i) after i * tau. The tolerance is expressed | ||||
// as a percentage, and we add a (large) 0.1% margin to account for | // as a percentage, and we add a (large) 0.1% margin to account for | ||||
// floating point errors. | // floating point errors. | ||||
BOOST_CHECK_CLOSE(previousScore, -1 * std::expm1(-1. * i), 100.1 / tau); | BOOST_CHECK_CLOSE(previousScore, -1 * std::expm1(-1. * i), 100.1 / tau); | ||||
} | } | ||||
// After 10 tau we should be very close to 100% (about 99.995%) | // After 10 tau we should be very close to 100% (about 99.995%) | ||||
BOOST_CHECK_CLOSE(previousScore, 1., 0.01); | BOOST_CHECK_CLOSE(previousScore, 1., 0.01); | ||||
for (size_t i = 1; i <= 3; i++) { | for (size_t i = 1; i <= 3; i++) { | ||||
for (uint32_t j = 0; j < tau; j += step) { | for (uint32_t j = 0; j < tau; j += step) { | ||||
avastats.invsPolled(2); | // target score = 2 * votes - polls - 1 | ||||
// => target score = 0 with votes = polls = 1 | |||||
avastats.invsPolled(1); | |||||
// Stop responding to the polls. | // Stop responding to the polls. | ||||
avastats.invsVoted(1); | avastats.invsVoted(1); | ||||
avastats.updateAvailabilityScore(); | avastats.updateAvailabilityScore(); | ||||
// Expect a monotonic fall | // Expect a monotonic fall | ||||
double currentScore = avastats.getAvailabilityScore(); | double currentScore = avastats.getAvailabilityScore(); | ||||
BOOST_CHECK_LE(currentScore, previousScore); | BOOST_CHECK_LE(currentScore, previousScore); | ||||
previousScore = currentScore; | previousScore = currentScore; | ||||
} | } | ||||
// There is a slight error in the expected value because we did not | // There is a slight error in the expected value because we did not | ||||
// start the decay at exactly 100%, but the 0.1% margin is at least an | // start the decay at exactly 100%, but the 0.1% margin is at least an | ||||
// order of magnitude larger than the expected error so it doesn't | // order of magnitude larger than the expected error so it doesn't | ||||
// matter. | // matter. | ||||
BOOST_CHECK_CLOSE(previousScore, 1. + std::expm1(-1. * i), 100.1 / tau); | BOOST_CHECK_CLOSE(previousScore, 1. + std::expm1(-1. * i), 100.1 / tau); | ||||
} | } | ||||
// After 3 more tau we should be under 5% | // After 3 more tau we should be under 5% | ||||
BOOST_CHECK_LT(previousScore, .05); | BOOST_CHECK_LT(previousScore, .05); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(avalanche_statistics_no_poll) { | |||||
const uint32_t step = AVALANCHE_STATISTICS_REFRESH_PERIOD.count(); | |||||
const uint32_t tau = AVALANCHE_STATISTICS_TIME_CONSTANT.count(); | |||||
CNode::AvalancheState avastats; | |||||
double previousScore = avastats.getAvailabilityScore(); | |||||
BOOST_CHECK_SMALL(previousScore, 1e-6); | |||||
// Check the statistics follow an exponential response for 1 to 10 tau | |||||
for (size_t i = 1; i <= 10; i++) { | |||||
for (uint32_t j = 0; j < tau; j += step) { | |||||
// target score = 2 * votes - polls - 1 | |||||
// => target score = -1 with votes = polls = 0 | |||||
avastats.updateAvailabilityScore(); | |||||
// Expect a monotonic faill | |||||
double currentScore = avastats.getAvailabilityScore(); | |||||
BOOST_CHECK_LT(currentScore, 0.); | |||||
BOOST_CHECK_LE(currentScore, previousScore); | |||||
previousScore = currentScore; | |||||
} | |||||
// We expect -(1 - e^-i) after i * tau. The tolerance is expressed | |||||
// as a percentage, and we add a (large) 0.1% margin to account for | |||||
// floating point errors. | |||||
BOOST_CHECK_CLOSE(previousScore, std::expm1(-1. * i), 100.1 / tau); | |||||
} | |||||
// After 10 tau we should be very close to -1 (about 99.995%) | |||||
BOOST_CHECK_CLOSE(previousScore, -1., 0.01); | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |