Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/voterecord_tests.cpp
// Copyright (c) 2018-2022 The Bitcoin developers | // Copyright (c) 2018-2022 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. | ||||
#include <avalanche/voterecord.h> | #include <avalanche/voterecord.h> | ||||
#include <test/util/setup_common.h> | #include <test/util/setup_common.h> | ||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
using namespace avalanche; | using namespace avalanche; | ||||
NodeId nextNodeId(NodeId &nodeid) { | struct VoteRecordFixture { | ||||
nodeid++; | NodeId currentNodeId = -1; | ||||
if (nodeid >= 8) { | |||||
nodeid = 0; | NodeId nextNodeId() { | ||||
currentNodeId++; | |||||
if (currentNodeId >= 8) { | |||||
currentNodeId = 0; | |||||
} | } | ||||
return nodeid; | return currentNodeId; | ||||
} | } | ||||
}; | |||||
BOOST_FIXTURE_TEST_SUITE(voterecord_tests, TestingSetup) | BOOST_FIXTURE_TEST_SUITE(voterecord_tests, VoteRecordFixture) | ||||
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, stale, confidence) \ | #define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, stale, confidence) \ | ||||
vr.registerVote(nextNodeId(nodeid), vote); \ | vr.registerVote(nextNodeId(), vote); \ | ||||
BOOST_CHECK_EQUAL(vr.isAccepted(), state); \ | BOOST_CHECK_EQUAL(vr.isAccepted(), state); \ | ||||
BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \ | BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \ | ||||
BOOST_CHECK_EQUAL(vr.isStale(), stale); \ | BOOST_CHECK_EQUAL(vr.isStale(), stale); \ | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), confidence); | BOOST_CHECK_EQUAL(vr.getConfidence(), confidence); | ||||
BOOST_AUTO_TEST_CASE(vote_record) { | BOOST_AUTO_TEST_CASE(vote_record) { | ||||
NodeId nodeid = -1; | |||||
VoteRecord vraccepted(true); | VoteRecord vraccepted(true); | ||||
// Check initial state. | // Check initial state. | ||||
BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true); | BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true); | ||||
BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false); | BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false); | ||||
BOOST_CHECK_EQUAL(vraccepted.isStale(), false); | BOOST_CHECK_EQUAL(vraccepted.isStale(), false); | ||||
BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0); | BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0); | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | for (int i = 1; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) { | ||||
BOOST_CHECK(vrinflight.registerPoll()); | BOOST_CHECK(vrinflight.registerPoll()); | ||||
BOOST_CHECK(!vrinflight.shouldPoll()); | BOOST_CHECK(!vrinflight.shouldPoll()); | ||||
} | } | ||||
} | } | ||||
// Test some cases where confidence never advances | // Test some cases where confidence never advances | ||||
BOOST_AUTO_TEST_CASE(stale_vote_always_inconclusive) { | BOOST_AUTO_TEST_CASE(stale_vote_always_inconclusive) { | ||||
NodeId nodeid = -1; | |||||
// Setup a record that is inconclusive so far | // Setup a record that is inconclusive so far | ||||
VoteRecord vr(false); | VoteRecord vr(false); | ||||
for (uint32_t i = 0; i < AVALANCHE_VOTE_STALE_THRESHOLD / 8; i++) { | for (uint32_t i = 0; i < AVALANCHE_VOTE_STALE_THRESHOLD / 8; i++) { | ||||
// Vote randomly, but such that there's always enough neutral votes to | // Vote randomly, but such that there's always enough neutral votes to | ||||
// not gain confidence. | // not gain confidence. | ||||
for (auto j = 0; j < 6; j++) { | for (auto j = 0; j < 6; j++) { | ||||
REGISTER_VOTE_AND_CHECK(vr, InsecureRand32(), false, false, false, | REGISTER_VOTE_AND_CHECK(vr, InsecureRand32(), false, false, false, | ||||
0); | 0); | ||||
} | } | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0); | REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0); | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0); | REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0); | ||||
} | } | ||||
// Vote record becomes stale after too many rounds of inconclusive voting | // Vote record becomes stale after too many rounds of inconclusive voting | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 0); | REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 0); | ||||
} | } | ||||
// Test all cases where records reach a specific confidence level and then go | // Test all cases where records reach a specific confidence level and then go | ||||
// stale. | // stale. | ||||
BOOST_AUTO_TEST_CASE(stale_vote_at_all_confidence_levels) { | BOOST_AUTO_TEST_CASE(stale_vote_at_all_confidence_levels) { | ||||
NodeId nodeid = -1; | |||||
for (uint32_t vote = 0; vote <= 1; vote++) { | for (uint32_t vote = 0; vote <= 1; vote++) { | ||||
for (uint32_t confidence = 0; confidence < AVALANCHE_FINALIZATION_SCORE; | for (uint32_t confidence = 0; confidence < AVALANCHE_FINALIZATION_SCORE; | ||||
confidence++) { | confidence++) { | ||||
VoteRecord vr(!vote); | VoteRecord vr(!vote); | ||||
// Prepare to increase confidence with some votes | // Prepare to increase confidence with some votes | ||||
for (auto i = 0; i < 5; i++) { | for (auto i = 0; i < 5; i++) { | ||||
REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0); | REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0); | ||||
Show All 27 Lines | for (uint32_t vote = 0; vote <= 1; vote++) { | ||||
} | } | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, true, confidence); | REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, true, confidence); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Test some cases where confidence may flip flop and then goes stale. | // Test some cases where confidence may flip flop and then goes stale. | ||||
BOOST_AUTO_TEST_CASE(stale_vote_random_then_inconclusive) { | BOOST_AUTO_TEST_CASE(stale_vote_random_then_inconclusive) { | ||||
NodeId nodeid = -1; | |||||
VoteRecord vr(false); | VoteRecord vr(false); | ||||
for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 14; i++) { | for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 14; i++) { | ||||
// Vote randomly. Confidence changes are ok. | // Vote randomly. Confidence changes are ok. | ||||
vr.registerVote(nextNodeId(nodeid), InsecureRand32()); | vr.registerVote(nextNodeId(), InsecureRand32()); | ||||
BOOST_CHECK_EQUAL(vr.hasFinalized(), false); | BOOST_CHECK_EQUAL(vr.hasFinalized(), false); | ||||
BOOST_CHECK_EQUAL(vr.isStale(), false); | BOOST_CHECK_EQUAL(vr.isStale(), false); | ||||
} | } | ||||
// Reset confidence, no matter what it is right now | // Reset confidence, no matter what it is right now | ||||
for (uint32_t i = 0; i < 7; i++) { | for (uint32_t i = 0; i < 7; i++) { | ||||
vr.registerVote(nextNodeId(nodeid), 0); | vr.registerVote(nextNodeId(), 0); | ||||
} | } | ||||
for (uint32_t i = 0; i < 7; i++) { | for (uint32_t i = 0; i < 7; i++) { | ||||
vr.registerVote(nextNodeId(nodeid), 1); | vr.registerVote(nextNodeId(), 1); | ||||
} | } | ||||
BOOST_CHECK_EQUAL(vr.hasFinalized(), false); | BOOST_CHECK_EQUAL(vr.hasFinalized(), false); | ||||
BOOST_CHECK_EQUAL(vr.isStale(), false); | BOOST_CHECK_EQUAL(vr.isStale(), false); | ||||
// Remainder of votes are neutral | // Remainder of votes are neutral | ||||
for (uint32_t i = 0; | for (uint32_t i = 0; | ||||
i < AVALANCHE_VOTE_STALE_THRESHOLD - AVALANCHE_FINALIZATION_SCORE; | i < AVALANCHE_VOTE_STALE_THRESHOLD - AVALANCHE_FINALIZATION_SCORE; | ||||
i++) { | i++) { | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1); | REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1); | ||||
} | } | ||||
// Vote record becomes stale after too many rounds of voting | // Vote record becomes stale after too many rounds of voting | ||||
REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 1); | REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 1); | ||||
} | } | ||||
// Test all cases where confidence flips as much as possible, ending at all | // Test all cases where confidence flips as much as possible, ending at all | ||||
// possible confidence levels. | // possible confidence levels. | ||||
BOOST_AUTO_TEST_CASE(stale_vote_with_confidence_flips) { | BOOST_AUTO_TEST_CASE(stale_vote_with_confidence_flips) { | ||||
NodeId nodeid = -1; | |||||
// Start testing with yes or no votes | // Start testing with yes or no votes | ||||
for (uint32_t voteInit = 0; voteInit <= 1; voteInit++) { | for (uint32_t voteInit = 0; voteInit <= 1; voteInit++) { | ||||
// Test stalling at all confidence levels | // Test stalling at all confidence levels | ||||
for (auto offset = 0; offset < AVALANCHE_FINALIZATION_SCORE; offset++) { | for (auto offset = 0; offset < AVALANCHE_FINALIZATION_SCORE; offset++) { | ||||
uint32_t vote = voteInit; | uint32_t vote = voteInit; | ||||
VoteRecord vr(!vote); | VoteRecord vr(!vote); | ||||
uint32_t count = 0; | uint32_t count = 0; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | for (uint32_t voteInit = 0; voteInit <= 1; voteInit++) { | ||||
finalsanitycheck: | finalsanitycheck: | ||||
BOOST_CHECK(vr.isStale()); | BOOST_CHECK(vr.isStale()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(duplicate_votes) { | BOOST_AUTO_TEST_CASE(duplicate_votes) { | ||||
VoteRecord vr(true); | VoteRecord vr(true); | ||||
NodeId nodeid = -1; | |||||
// Register some votes, expecting confidence to increase | // Register some votes, expecting confidence to increase | ||||
for (auto i = 0; i < 7; i++) { | for (auto i = 0; i < 7; i++) { | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), 0); | BOOST_CHECK_EQUAL(vr.getConfidence(), 0); | ||||
BOOST_CHECK(!vr.registerVote(nextNodeId(nodeid), 0)); | BOOST_CHECK(!vr.registerVote(nextNodeId(), 0)); | ||||
} | } | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), 1); | BOOST_CHECK_EQUAL(vr.getConfidence(), 1); | ||||
// Multiple duplicate votes do not advance confidence | // Multiple duplicate votes do not advance confidence | ||||
for (auto i = 0; i < 8; i++) { | for (auto i = 0; i < 8; i++) { | ||||
BOOST_CHECK(!vr.registerVote(nodeid, 0)); | BOOST_CHECK(!vr.registerVote(currentNodeId, 0)); | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), 1); | BOOST_CHECK_EQUAL(vr.getConfidence(), 1); | ||||
} | } | ||||
// Register more votes with duplicates mixed in. Confidence should only | // Register more votes with duplicates mixed in. Confidence should only | ||||
// increase when duplicates are not used. | // increase when duplicates are not used. | ||||
auto expectedConfidence = 1; | auto expectedConfidence = 1; | ||||
for (auto i = 0; i < 8; i++) { | for (auto i = 0; i < 8; i++) { | ||||
BOOST_CHECK(!vr.registerVote(nodeid, 0)); | BOOST_CHECK(!vr.registerVote(currentNodeId, 0)); | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), expectedConfidence); | BOOST_CHECK_EQUAL(vr.getConfidence(), expectedConfidence); | ||||
for (auto j = i; j < 8; j++) { | for (auto j = i; j < 8; j++) { | ||||
BOOST_CHECK(!vr.registerVote(nextNodeId(nodeid), 0)); | BOOST_CHECK(!vr.registerVote(nextNodeId(), 0)); | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence); | BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence); | ||||
} | } | ||||
} | } | ||||
// Register enough votes to get just before finalization | // Register enough votes to get just before finalization | ||||
for (auto i = 0; i < 90; i++) { | for (auto i = 0; i < 90; i++) { | ||||
BOOST_CHECK(!vr.registerVote(nextNodeId(nodeid), 0)); | BOOST_CHECK(!vr.registerVote(nextNodeId(), 0)); | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence); | BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence); | ||||
} | } | ||||
// Sanity check that finalization occurs on the expected vote | // Sanity check that finalization occurs on the expected vote | ||||
BOOST_CHECK(vr.registerVote(nextNodeId(nodeid), 0)); | BOOST_CHECK(vr.registerVote(nextNodeId(), 0)); | ||||
BOOST_CHECK(vr.hasFinalized()); | BOOST_CHECK(vr.hasFinalized()); | ||||
} | } | ||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |