Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/peermanager_tests.cpp
Show All 27 Lines | struct TestPeerManager { | ||||
}); | }); | ||||
} | } | ||||
static bool isNodePending(const PeerManager &pm, NodeId nodeid) { | static bool isNodePending(const PeerManager &pm, NodeId nodeid) { | ||||
auto &pendingNodesView = pm.pendingNodes.get<by_nodeid>(); | auto &pendingNodesView = pm.pendingNodes.get<by_nodeid>(); | ||||
return pendingNodesView.find(nodeid) != pendingNodesView.end(); | return pendingNodesView.find(nodeid) != pendingNodesView.end(); | ||||
} | } | ||||
static PeerId getPeerIdForProofId(PeerManager &pm, | |||||
const ProofId &proofid) { | |||||
auto &pview = pm.peers.get<by_proofid>(); | |||||
auto it = pview.find(proofid); | |||||
return it == pview.end() ? NO_PEER : it->peerid; | |||||
} | |||||
static PeerId registerAndGetPeerId(PeerManager &pm, | static PeerId registerAndGetPeerId(PeerManager &pm, | ||||
const ProofRef &proof) { | const ProofRef &proof) { | ||||
pm.registerProof(proof); | pm.registerProof(proof); | ||||
return getPeerIdForProofId(pm, proof->getId()); | |||||
Fabien: I don't see the point of splitting this, there is no reason not to use the actual facility… | |||||
tyler-smithAuthorUnsubmitted Not Done Inline ActionsThe peerid changes when the peer is moved out of and back into the valid pool, so getting the peerid when it's first registered doesn't help. tyler-smith: The peerid changes when the peer is moved out of and back into the valid pool, so getting the… | |||||
FabienUnsubmitted Not Done Inline ActionsOK Fabien: OK | |||||
auto &pview = pm.peers.get<by_proofid>(); | |||||
auto it = pview.find(proof->getId()); | |||||
return it == pview.end() ? NO_PEER : it->peerid; | |||||
} | } | ||||
static std::vector<uint32_t> getOrderedScores(const PeerManager &pm) { | static std::vector<uint32_t> getOrderedScores(const PeerManager &pm) { | ||||
std::vector<uint32_t> scores; | std::vector<uint32_t> scores; | ||||
auto &peerView = pm.peers.get<by_score>(); | auto &peerView = pm.peers.get<by_score>(); | ||||
for (const Peer &peer : peerView) { | for (const Peer &peer : peerView) { | ||||
scores.push_back(peer.getScore()); | scores.push_back(peer.getScore()); | ||||
▲ Show 20 Lines • Show All 1,509 Lines • ▼ Show 20 Lines | for (auto &proof : proofs) { | ||||
BOOST_CHECK(pm.registerProof(proof)); | BOOST_CHECK(pm.registerProof(proof)); | ||||
} | } | ||||
auto peersScores = TestPeerManager::getOrderedScores(pm); | auto peersScores = TestPeerManager::getOrderedScores(pm); | ||||
BOOST_CHECK_EQUAL_COLLECTIONS(peersScores.begin(), peersScores.end(), | BOOST_CHECK_EQUAL_COLLECTIONS(peersScores.begin(), peersScores.end(), | ||||
expectedScores.begin(), expectedScores.end()); | expectedScores.begin(), expectedScores.end()); | ||||
} | } | ||||
BOOST_FIXTURE_TEST_CASE(known_score_tracking, NoCoolDownFixture) { | |||||
avalanche::PeerManager pm; | |||||
const CKey key = CKey::MakeCompressedKey(); | |||||
const Amount amount10(10 * COIN); | |||||
const Amount amount20(20 * COIN); | |||||
const uint32_t height = 100; | |||||
const bool is_coinbase = false; | |||||
CScript script = GetScriptForDestination(PKHash(key.GetPubKey())); | |||||
const COutPoint peer1ConflictingOutput(TxId(GetRandHash()), 0); | |||||
{ | |||||
LOCK(cs_main); | |||||
CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | |||||
coins.AddCoin(peer1ConflictingOutput, | |||||
Coin(CTxOut(amount10, script), height, is_coinbase), | |||||
false); | |||||
} | |||||
const COutPoint peer1SecondaryOutpoint(TxId(GetRandHash()), 1); | |||||
{ | |||||
LOCK(cs_main); | |||||
CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | |||||
coins.AddCoin(peer1SecondaryOutpoint, | |||||
Coin(CTxOut(amount20, script), height, is_coinbase), | |||||
false); | |||||
} | |||||
auto buildProofWithSequenceAndOutpoints = | |||||
[&](int64_t sequence, | |||||
const std::vector<std::tuple<COutPoint, Amount>> &outpoints) { | |||||
ProofBuilder pb(sequence, 0, key); | |||||
for (const auto &outpoint : outpoints) { | |||||
BOOST_CHECK(pb.addUTXO(std::get<0>(outpoint), | |||||
std::get<1>(outpoint), height, | |||||
is_coinbase, key)); | |||||
} | |||||
return pb.build(); | |||||
}; | |||||
auto peer1Proof1 = buildProofWithSequenceAndOutpoints( | |||||
10, {{peer1ConflictingOutput, amount10}, | |||||
{peer1SecondaryOutpoint, amount20}}); | |||||
auto peer1Proof2 = buildProofWithSequenceAndOutpoints( | |||||
20, {{peer1ConflictingOutput, amount10}}); | |||||
auto peer1Proof3 = buildProofWithSequenceAndOutpoints( | |||||
30, {{peer1ConflictingOutput, amount10}, | |||||
{COutPoint{TxId(GetRandHash()), 0}, amount10}}); | |||||
const uint32_t peer1Score1 = Proof::amountToScore(amount10 + amount20); | |||||
const uint32_t peer1Score2 = Proof::amountToScore(amount10); | |||||
// Add first peer and check that we have its score tracked | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), 0); | |||||
BOOST_CHECK(pm.registerProof(peer1Proof2)); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
// Ensure failing to add conflicting proofs doesn't affect the score, the | |||||
// first proof stays bound and counted | |||||
BOOST_CHECK(!pm.registerProof(peer1Proof1)); | |||||
BOOST_CHECK(!pm.registerProof(peer1Proof3)); | |||||
BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId())); | |||||
BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId())); | |||||
BOOST_CHECK(pm.isOrphan(peer1Proof3->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
auto checkRejectDefault = [&](const ProofId &proofid) { | |||||
BOOST_CHECK(pm.exists(proofid)); | |||||
const bool isOrphan = pm.isOrphan(proofid); | |||||
BOOST_CHECK(pm.rejectProof( | |||||
proofid, avalanche::PeerManager::RejectionMode::DEFAULT)); | |||||
BOOST_CHECK(!pm.isBoundToPeer(proofid)); | |||||
BOOST_CHECK_EQUAL(pm.exists(proofid), !isOrphan); | |||||
}; | |||||
auto checkRejectInvalidate = [&](const ProofId &proofid) { | |||||
BOOST_CHECK(pm.exists(proofid)); | |||||
BOOST_CHECK(pm.rejectProof( | |||||
proofid, avalanche::PeerManager::RejectionMode::INVALIDATE)); | |||||
}; | |||||
// Reject from the orphan pool doesn't affect tracked score | |||||
checkRejectDefault(peer1Proof3->getId()); | |||||
BOOST_CHECK(!pm.registerProof(peer1Proof3)); | |||||
BOOST_CHECK(pm.isOrphan(peer1Proof3->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
checkRejectInvalidate(peer1Proof3->getId()); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
// Reject from the conflicting pool | |||||
checkRejectDefault(peer1Proof1->getId()); | |||||
checkRejectInvalidate(peer1Proof1->getId()); | |||||
// Add again a proof to the conflicting pool | |||||
BOOST_CHECK(!pm.registerProof(peer1Proof1)); | |||||
BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
// Reject from the valid pool, default mode | |||||
// Now the score should change as the new peer is promoted | |||||
checkRejectDefault(peer1Proof2->getId()); | |||||
BOOST_CHECK(!pm.isInConflictingPool(peer1Proof1->getId())); | |||||
BOOST_CHECK(pm.isBoundToPeer(peer1Proof1->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score1); | |||||
// Reject from the valid pool, invalidate mode | |||||
// Now the score should change as the old peer is re-promoted | |||||
checkRejectInvalidate(peer1Proof1->getId()); | |||||
// The conflicting proof should also be promoted to a peer | |||||
BOOST_CHECK(!pm.isInConflictingPool(peer1Proof2->getId())); | |||||
BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
// Now add another peer and check that combined scores are correct | |||||
uint32_t peer2Score = 1 * MIN_VALID_PROOF_SCORE; | |||||
auto peer2Proof1 = buildRandomProof(peer2Score); | |||||
PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, peer2Proof1); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2 + peer2Score); | |||||
// Trying to remove non-existent peer doesn't affect score | |||||
BOOST_CHECK(!pm.removePeer(1234)); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2 + peer2Score); | |||||
// Removing new peer removes its score | |||||
BOOST_CHECK(pm.removePeer(peerid2)); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), peer1Score2); | |||||
PeerId peerid1 = | |||||
TestPeerManager::getPeerIdForProofId(pm, peer1Proof2->getId()); | |||||
BOOST_CHECK(pm.removePeer(peerid1)); | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), 0); | |||||
} | |||||
BOOST_AUTO_TEST_CASE(connected_score_tracking) { | |||||
avalanche::PeerManager pm; | |||||
const auto checkScores = [&pm](uint32_t known, uint32_t connected) { | |||||
BOOST_CHECK_EQUAL(pm.getKnownPeersScore(), known); | |||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), connected); | |||||
}; | |||||
// Start out with 0s | |||||
checkScores(0, 0); | |||||
// Create one peer without a node. Its score should be registered but not | |||||
// connected | |||||
uint32_t score1 = 10000000 * MIN_VALID_PROOF_SCORE; | |||||
auto proof1 = buildRandomProof(score1); | |||||
PeerId peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1); | |||||
checkScores(score1, 0); | |||||
// Add nodes. We now have a connected score, but it doesn't matter how many | |||||
// nodes we add the score is the same | |||||
const ProofId &proofid1 = proof1->getId(); | |||||
const uint8_t nodesToAdd = 10; | |||||
for (int i = 0; i < nodesToAdd; i++) { | |||||
BOOST_CHECK(pm.addNode(i, proofid1)); | |||||
checkScores(score1, score1); | |||||
} | |||||
// Remove all but 1 node and ensure the score doesn't change | |||||
for (int i = 0; i < nodesToAdd - 1; i++) { | |||||
BOOST_CHECK(pm.removeNode(i)); | |||||
checkScores(score1, score1); | |||||
} | |||||
// Removing the last node should remove the score from the connected count | |||||
BOOST_CHECK(pm.removeNode(nodesToAdd - 1)); | |||||
checkScores(score1, 0); | |||||
// Add 2 nodes to peer and create peer2. Without a node peer2 has no | |||||
// connected score but after adding a node it does. | |||||
BOOST_CHECK(pm.addNode(0, proofid1)); | |||||
BOOST_CHECK(pm.addNode(1, proofid1)); | |||||
checkScores(score1, score1); | |||||
uint32_t score2 = 1 * MIN_VALID_PROOF_SCORE; | |||||
auto proof2 = buildRandomProof(score2); | |||||
PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2); | |||||
checkScores(score1 + score2, score1); | |||||
BOOST_CHECK(pm.addNode(2, proof2->getId())); | |||||
checkScores(score1 + score2, score1 + score2); | |||||
// The first peer has two nodes left. Remove one and nothing happens, remove | |||||
// the other and its score is no longer in the connected counter.. | |||||
BOOST_CHECK(pm.removeNode(0)); | |||||
checkScores(score1 + score2, score1 + score2); | |||||
BOOST_CHECK(pm.removeNode(1)); | |||||
checkScores(score1 + score2, score2); | |||||
// Removing a peer with no allocated score has no affect. | |||||
BOOST_CHECK(pm.removePeer(peerid1)); | |||||
checkScores(score2, score2); | |||||
// Remove the second peer's node removes its allocated score. | |||||
BOOST_CHECK(pm.removeNode(2)); | |||||
checkScores(score2, 0); | |||||
// Removing the second peer takes us back to 0. | |||||
BOOST_CHECK(pm.removePeer(peerid2)); | |||||
checkScores(0, 0); | |||||
// Add 2 peers with nodes and remove them without removing the nodes first. | |||||
// Both score counters should be reduced by each peer's score when it's | |||||
// removed. | |||||
peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1); | |||||
checkScores(score1, 0); | |||||
peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2); | |||||
checkScores(score1 + score2, 0); | |||||
BOOST_CHECK(pm.addNode(0, proof1->getId())); | |||||
checkScores(score1 + score2, score1); | |||||
BOOST_CHECK(pm.addNode(1, proof2->getId())); | |||||
checkScores(score1 + score2, score1 + score2); | |||||
BOOST_CHECK(pm.removePeer(peerid2)); | |||||
checkScores(score1, score1); | |||||
BOOST_CHECK(pm.removePeer(peerid1)); | |||||
checkScores(0, 0); | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |
I don't see the point of splitting this, there is no reason not to use the actual facility directly.
You can still check the registration is successful with BOOST_CHECK_NE(TestPeerManager::registerAndGetPeerId(pm, proof), NO_PEER);