21#include <validation.h>
23#include <test/util/blockindex.h>
24#include <test/util/random.h>
25#include <test/util/setup_common.h>
27#include <boost/test/unit_test.hpp>
31#include <unordered_map>
37 struct TestPeerManager {
41 return node.peerid == peerid;
46 auto &pendingNodesView = pm.
pendingNodes.get<by_nodeid>();
47 return pendingNodesView.find(nodeid) != pendingNodesView.end();
52 auto &pview = pm.
peers.get<by_proofid>();
53 auto it = pview.find(proofid);
54 return it == pview.end() ?
NO_PEER : it->peerid;
60 return getPeerIdForProofId(pm, proof->
getId());
63 static std::vector<uint32_t> getOrderedScores(
const PeerManager &pm) {
64 std::vector<uint32_t> scores;
66 auto &peerView = pm.
peers.get<by_score>();
67 for (
const Peer &peer : peerView) {
68 scores.push_back(peer.getScore());
74 static void cleanupDanglingProofs(
76 std::unordered_set<ProofRef, SaltedProofHasher> ®isteredProofs) {
80 static void cleanupDanglingProofs(
PeerManager &pm) {
81 std::unordered_set<ProofRef, SaltedProofHasher> dummy;
85 static std::optional<RemoteProof> getRemoteProof(
const PeerManager &pm,
88 auto it = pm.
remoteProofs.find(boost::make_tuple(proofid, nodeid));
92 return std::make_optional(*it);
96 return pm.
peers.size();
99 static std::optional<bool>
105 std::vector<PeerId> peerIds;
106 for (
auto &peer : pm.
peers) {
107 peerIds.push_back(peer.peerid);
109 for (
const PeerId &peerid : peerIds) {
131 uint32_t height = 100,
bool is_coinbase =
false) {
140 static COutPoint createUtxo(
Chainstate &chainstate,
const CKey &key,
142 uint32_t height = 100,
143 bool is_coinbase =
false) {
145 addCoin(chainstate, outpoint, key, amount, height, is_coinbase);
150 buildProof(
const CKey &key,
151 const std::vector<std::tuple<COutPoint, Amount>> &outpoints,
153 int64_t sequence = 1, uint32_t height = 100,
154 bool is_coinbase =
false, int64_t expirationTime = 0,
156 ProofBuilder pb(sequence, expirationTime, master, payoutScript);
157 for (
const auto &[outpoint, amount] : outpoints) {
158 BOOST_CHECK(pb.addUTXO(outpoint, amount, height, is_coinbase, key));
163 template <
typename... Args>
165 buildProofWithOutpoints(
const CKey &key,
166 const std::vector<COutPoint> &outpoints,
167 Amount amount, Args &&...args) {
168 std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount;
170 outpoints.begin(), outpoints.end(),
171 std::back_inserter(outpointsWithAmount),
172 [amount](
const auto &o) { return std::make_tuple(o, amount); });
173 return buildProof(key, outpointsWithAmount,
174 std::forward<Args>(args)...);
178 buildProofWithSequence(
const CKey &key,
179 const std::vector<COutPoint> &outpoints,
188struct PeerManagerFixture :
public TestChain100Setup {
189 PeerManagerFixture() {
192 ~PeerManagerFixture() {
199struct NoCoolDownFixture :
public PeerManagerFixture {
200 NoCoolDownFixture() {
203 ~NoCoolDownFixture() {
209BOOST_FIXTURE_TEST_SUITE(peermanager_tests, PeerManagerFixture)
217 const std::vector<Slot> oneslot = {{100, 100, 23}};
235 const std::vector<Slot> twoslots = {{100, 100, 69}, {300, 100, 42}};
264 std::vector<Slot> slots;
268 for (
int i = 0; i < 100; i++) {
269 slots.emplace_back(max, 1, i);
276 for (
int i = 0; i < 100; i++) {
284 slots[99] = slots[99].withScore(101);
285 max = slots[99].getStop();
288 for (
int i = 0; i < 100; i++) {
299 for (
int i = 0; i < 100; i++) {
300 slots[i] = slots[i].withStart(slots[i].getStart() + 100);
303 slots[0] =
Slot(1, slots[0].getStop() - 1, slots[0].getPeerId());
304 slots[99] = slots[99].withScore(1);
305 max = slots[99].getStop();
312 for (
int i = 0; i < 100; i++) {
319 for (
int c = 0; c < 1000; c++) {
320 size_t size = m_rng.randbits(10) + 1;
321 std::vector<Slot> slots;
324 uint64_t max = m_rng.randbits(3);
327 max += m_rng.randbits(3);
331 for (
size_t i = 0; i < size; i++) {
332 const uint64_t start = next();
333 const uint32_t score = m_rng.randbits(3);
335 slots.emplace_back(start, score, i);
338 for (
int k = 0; k < 100; k++) {
339 uint64_t s = max > 0 ? m_rng.randrange(max) : 0;
363 const NodeId node0 = 42, node1 = 69, node2 = 37;
373 std::unordered_map<PeerId, int> results = {};
374 for (
int i = 0; i < 10000; i++) {
380 BOOST_CHECK(abs(2 * results[0] - results[1]) < 500);
386 for (
int i = 0; i < 10000; i++) {
388 BOOST_CHECK(n == node0 || n == node1 || n == node2);
392 BOOST_CHECK(abs(results[0] - results[1] + results[2]) < 500);
403 std::array<PeerId, 8> peerids;
404 for (
int i = 0; i < 4; i++) {
406 peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
414 for (
int i = 0; i < 100; i++) {
416 BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[2] ||
431 for (
int i = 0; i < 100; i++) {
433 BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[3]);
437 for (
int i = 0; i < 4; i++) {
439 peerids[i + 4] = TestPeerManager::registerAndGetPeerId(pm, p);
462 for (
int i = 0; i < 100; i++) {
464 BOOST_CHECK(p == peerids[1] || p == peerids[3] || p == peerids[4] ||
465 p == peerids[5] || p == peerids[6]);
480 std::array<PeerId, 4> peerids;
481 for (
int i = 0; i < 4; i++) {
484 peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
490 for (
auto p : peerids) {
497 for (
int i = 0; i < 100; i++) {
512 const std::array<uint32_t, 4> scores{{10000, 20000, 30000, 40000}};
513 std::array<PeerId, 4> peerids;
514 for (
int i = 0; i < 4; i++) {
516 peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
540 for (uint64_t slot = 0; slot < pm.
getSlotCount(); slot++) {
541 PeerId p = TestPeerManager::selectPeerFromSlot(pm, slot);
544 }
else if (slot < 40000) {
566 for (
int i = 0; i < 4; i++) {
571 for (
int i = 0; i < 100; i++) {
575 n, Now<SteadyMilliseconds>(), round++));
581 for (
int i = 0; i < 100; i++) {
585 n, Now<SteadyMilliseconds>(), round++));
590 1, Now<SteadyMilliseconds>() + std::chrono::hours(24), round++));
592 for (
int i = 0; i < 100; i++) {
596 n, Now<SteadyMilliseconds>(), round++));
603 int node3selected = 0;
604 for (
int i = 0; i < 100; i++) {
613 n, Now<SteadyMilliseconds>(), round++));
617 for (
int i = 0; i < 100; i++) {
644 for (
int i = 0; i < 10; i++) {
647 BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
653 const PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
654 BOOST_CHECK_NE(peerid,
NO_PEER);
655 for (
int i = 0; i < 10; i++) {
656 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
657 BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
664 for (
int i = 0; i < 5; i++) {
666 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
667 BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
673 for (
int i = 0; i < 5; i++) {
675 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
676 BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
682 const ProofId &alt_proofid = alt_proof->getId();
685 for (
int i = 0; i < 5; i++) {
688 BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
689 BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
696 const ProofId &alt2_proofid = alt2_proof->getId();
699 for (
int i = 0; i < 5; i++) {
702 BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
708 for (
int i = 0; i < 5; i++) {
710 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
711 BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
719 for (
int i = 0; i < 10; i++) {
720 BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
721 BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
728 for (
int i = 0; i < 10; i++) {
730 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
731 BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
747 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
748 BOOST_CHECK_NE(peerid,
NO_PEER);
752 for (
int i = 0; i < 10; i++) {
754 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
755 BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
770 for (
int i = 0; i < 10; i++) {
771 BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
772 BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
792 peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
793 BOOST_CHECK_NE(peerid,
NO_PEER);
794 for (
int i = 0; i < 10; i++) {
795 BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
796 BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
809 const int height = 100;
812 for (uint32_t i = 0; i < 10; i++) {
819 const auto getPeerId = [&](
const std::vector<COutPoint> &outpoints) {
820 return TestPeerManager::registerAndGetPeerId(
821 pm, buildProofWithOutpoints(key, outpoints, v, masterKey, 0, height,
826 const PeerId peer1 = getPeerId({COutPoint(txid1, 0)});
833 const PeerId peer2 = getPeerId({COutPoint(txid2, 0)});
837 const PeerId peer3 = getPeerId({COutPoint(txid1, 1)});
844 const PeerId peer4 = getPeerId({COutPoint(txid1, 2), COutPoint(txid2, 2)});
851 COutPoint o(txid1, 3);
854 !pm.
registerProof(TestProofBuilder::buildDuplicatedStakes(pb)));
876 int immatureHeight = 100;
878 auto registerImmature = [&](
const ProofRef &proof) {
884 auto checkImmature = [&](
const ProofRef &proof,
bool expectedImmature) {
901 std::vector<ProofRef> immatureProofs;
906 auto proof = buildProofWithOutpoints(
910 registerImmature(proof);
911 checkImmature(proof,
true);
912 immatureProofs.push_back(proof);
916 for (
auto i = 0; i < 100; i++) {
920 key, 0, immatureHeight);
923 registerImmature(proof);
924 checkImmature(proof,
true);
925 immatureProofs.push_back(proof);
927 immatureProofs.erase(immatureProofs.begin());
932 const COutPoint &outpoint =
933 immatureProofs.front()->getStakes()[0].getStake().getUTXO();
936 key, 1, immatureHeight);
937 registerImmature(proof);
938 checkImmature(proof,
true);
939 immatureProofs.push_back(proof);
941 immatureProofs.erase(immatureProofs.begin());
948 for (
const auto &proof : immatureProofs) {
949 checkImmature(proof,
false);
960 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
961 BOOST_CHECK_NE(peerid,
NO_PEER);
964 std::chrono::hours(24));
967 for (
int i = 0; i < 10; i++) {
977 for (
int i = 0; i < 10; i++) {
983 peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
984 BOOST_CHECK_NE(peerid,
NO_PEER);
987 for (
int i = 0; i < 10; i++) {
991 i, [&](
const Node &n) { return n.nextRequestTime == theFuture; }));
998 for (
int i = 0; i < 10; i++) {
1007 constexpr int numProofs = 10;
1009 std::vector<ProofRef> proofs;
1010 proofs.reserve(numProofs);
1011 for (
int i = 0; i < numProofs; i++) {
1016 for (
int i = 0; i < numProofs; i++) {
1024 ProofRegistrationResult::ALREADY_REGISTERED);
1027 for (
int added = 0; added <= i; added++) {
1028 auto proof = pm.
getProof(proofs[added]->getId());
1037 const std::string badProofHex(
1038 "96527eae083f1f24625f049d9e54bb9a21023beefdde700a6bc02036335b4df141c8b"
1039 "c67bb05a971f5ac2745fd683797dde3002321023beefdde700a6bc02036335b4df141"
1040 "c8bc67bb05a971f5ac2745fd683797dde3ac135da984db510334abe41134e3d4ef09a"
1041 "d006b1152be8bc413182bf6f947eac1f8580fe265a382195aa2d73935cabf86d90a8f"
1042 "666d0a62385ae24732eca51575");
1045 BOOST_CHECK(Proof::FromHex(*badProof, badProofHex, error));
1060 const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
1061 const COutPoint outpointToSend = createUtxo(active_chainstate, key);
1064 buildProofWithSequence(key, {conflictingOutpoint, outpointToSend}, 20);
1068 buildProofWithSequence(key, {conflictingOutpoint}, 10);
1071 BOOST_CHECK(state.GetResult() == ProofRegistrationResult::CONFLICTING);
1093 const uint32_t height = 100;
1094 const bool is_coinbase =
false;
1100 auto conflictingOutpoint = createUtxo(active_chainstate, key, amount);
1102 auto proof_base = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1105 auto checkPreferred = [&](
const ProofRef &candidate,
1106 const ProofRef &reference,
bool expectAccepted) {
1118 ProofRegistrationResult::CONFLICTING,
1132 checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 9),
1135 checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 11),
1138 auto buildProofFromAmounts = [&](
const CKey &master,
1139 std::vector<Amount> &&amounts) {
1140 std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount{
1141 {conflictingOutpoint, amount}};
1142 std::transform(amounts.begin(), amounts.end(),
1143 std::back_inserter(outpointsWithAmount),
1144 [&key, &active_chainstate](
const Amount amount) {
1145 return std::make_tuple(
1146 createUtxo(active_chainstate, key, amount),
1149 return buildProof(key, outpointsWithAmount, master, 0, height,
1153 auto proof_multiUtxo = buildProofFromAmounts(
1160 checkPreferred(buildProofFromAmounts(
1162 proof_multiUtxo,
false);
1166 proof_multiUtxo,
true);
1169 proof_multiUtxo,
true);
1174 proof_multiUtxo,
false);
1176 auto proofSimilar = buildProofFromAmounts(
1178 checkPreferred(proofSimilar, proof_multiUtxo,
1179 proofSimilar->getId() < proof_multiUtxo->getId());
1192 const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
1193 const COutPoint matureOutpoint =
1196 auto immature10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1198 buildProofWithSequence(key, {conflictingOutpoint, matureOutpoint}, 20);
1208 auto proof30 = buildProofWithOutpoints(key, {matureOutpoint},
1236 const COutPoint conflictingOutpoint =
1239 auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1240 auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1241 auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1268 auto now = GetTime<std::chrono::seconds>();
1272 for (
size_t i = 0; i < 10; i++) {
1279 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
1281 auto checkNextPossibleConflictTime = [&](std::chrono::seconds expected) {
1283 return p.nextPossibleConflictTime == expected;
1287 checkNextPossibleConflictTime(now);
1291 peerid, now - std::chrono::seconds{1}));
1292 checkNextPossibleConflictTime(now);
1295 peerid, now + std::chrono::seconds{1}));
1296 checkNextPossibleConflictTime(now + std::chrono::seconds{1});
1305 const COutPoint conflictingOutpoint =
1308 auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1309 auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1310 auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1339 for (
size_t i = 0; i < 10; i++) {
1342 !pm.
registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1350 for (
size_t i = 0; i < 10; i++) {
1352 pm.
registerProof(proofSeq30, RegistrationMode::FORCE_ACCEPT));
1358 pm.
registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1371 const COutPoint conflictingOutpoint =
1374 auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1375 auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1376 auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1403 const COutPoint conflictingOutpoint =
1406 auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1407 auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1408 auto proofSeq40 = buildProofWithSequence(key, {conflictingOutpoint}, 40);
1410 int64_t conflictingProofCooldown = 100;
1412 strprintf(
"%d", conflictingProofCooldown));
1416 auto increaseMockTime = [&](int64_t s) {
1420 increaseMockTime(0);
1425 auto checkRegistrationFailure = [&](
const ProofRef &proof,
1434 checkRegistrationFailure(proofSeq20,
1435 ProofRegistrationResult::COOLDOWN_NOT_ELAPSED);
1439 checkRegistrationFailure(proofSeq40,
1440 ProofRegistrationResult::COOLDOWN_NOT_ELAPSED);
1444 increaseMockTime(conflictingProofCooldown);
1447 checkRegistrationFailure(proofSeq20, ProofRegistrationResult::CONFLICTING);
1451 checkRegistrationFailure(proofSeq40,
1452 ProofRegistrationResult::COOLDOWN_NOT_ELAPSED);
1457 increaseMockTime(conflictingProofCooldown);
1478 const COutPoint conflictingOutpoint =
1480 const COutPoint immatureOutpoint = createUtxo(active_chainstate, key);
1483 auto proofSeq10 = buildProofWithOutpoints(
1485 auto proofSeq20 = buildProofWithOutpoints(
1487 auto immature30 = buildProofWithSequence(
1488 key, {conflictingOutpoint, immatureOutpoint}, 30);
1499 for (
size_t i = 0; i < 10; i++) {
1508 auto checkRejectDefault = [&](
const ProofId &proofid) {
1510 const bool isImmature = pm.
isImmature(proofid);
1517 auto checkRejectInvalidate = [&](
const ProofId &proofid) {
1524 checkRejectDefault(immature30->getId());
1527 checkRejectInvalidate(immature30->getId());
1530 checkRejectDefault(proofSeq10->getId());
1531 checkRejectInvalidate(proofSeq10->getId());
1538 checkRejectDefault(proofSeq20->getId());
1545 checkRejectInvalidate(proofSeq10->getId());
1572 for (
size_t i = 0; i < 10; i++) {
1579 for (
size_t i = 0; i < 10; i++) {
1585 auto cooldownTimepoint = Now<SteadyMilliseconds>() + 10s;
1590 for (
size_t i = 0; i < 10; i++) {
1592 BOOST_CHECK_NE(selectedId,
NO_NODE);
1594 selectedId, cooldownTimepoint, round++));
1603 for (
size_t i = 0; i < 10; i++) {
1619 TestPeerManager::cleanupDanglingProofs(pm);
1626 TestPeerManager::cleanupDanglingProofs(pm);
1630 for (
size_t i = 0; i < 10; i++) {
1644 for (
size_t i = 0; i < 10; i++) {
1655 TestPeerManager::cleanupDanglingProofs(pm);
1661 TestPeerManager::cleanupDanglingProofs(pm);
1679 std::vector<uint32_t> expectedScores(10);
1681 std::generate(expectedScores.rbegin(), expectedScores.rend(),
1682 [n = 1]()
mutable { return n++ * MIN_VALID_PROOF_SCORE; });
1684 std::vector<ProofRef> proofs;
1685 proofs.reserve(expectedScores.size());
1686 for (uint32_t score : expectedScores) {
1692 for (
auto &proof : proofs) {
1696 auto peersScores = TestPeerManager::getOrderedScores(pm);
1697 BOOST_CHECK_EQUAL_COLLECTIONS(peersScores.begin(), peersScores.end(),
1698 expectedScores.begin(), expectedScores.end());
1713 const COutPoint peer1ConflictingOutput =
1714 createUtxo(active_chainstate, key, amount1, 99);
1715 const COutPoint peer1SecondaryOutpoint =
1716 createUtxo(active_chainstate, key, amount2, 99);
1718 auto peer1Proof1 = buildProof(
1720 {{peer1ConflictingOutput, amount1}, {peer1SecondaryOutpoint, amount2}},
1723 buildProof(key, {{peer1ConflictingOutput, amount1}}, key, 20, 99);
1728 {{peer1ConflictingOutput, amount1},
1729 {createUtxo(active_chainstate, key, amount1), amount1}},
1732 const uint32_t peer1Score1 = Proof::amountToScore(amount1 + amount2);
1733 const uint32_t peer1Score2 = Proof::amountToScore(amount1);
1751 auto checkRejectDefault = [&](
const ProofId &proofid) {
1753 const bool isImmature = pm.
isImmature(proofid);
1760 auto checkRejectInvalidate = [&](
const ProofId &proofid) {
1767 checkRejectDefault(peer1Proof3->getId());
1771 checkRejectInvalidate(peer1Proof3->getId());
1775 checkRejectDefault(peer1Proof1->getId());
1776 checkRejectInvalidate(peer1Proof1->getId());
1785 checkRejectDefault(peer1Proof2->getId());
1792 checkRejectInvalidate(peer1Proof1->getId());
1802 PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, peer2Proof1);
1813 TestPeerManager::getPeerIdForProofId(pm, peer1Proof2->getId());
1822 const auto checkScores = [&pm](uint32_t known, uint32_t connected) {
1836 PeerId peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1837 checkScores(score1, 0);
1841 const ProofId &proofid1 = proof1->getId();
1842 const uint8_t nodesToAdd = 10;
1843 for (
int i = 0; i < nodesToAdd; i++) {
1846 checkScores(score1, score1);
1850 for (
int i = 0; i < nodesToAdd - 1; i++) {
1852 checkScores(score1, score1);
1857 checkScores(score1, 0);
1863 checkScores(score1, score1);
1867 PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1868 checkScores(score1 + score2, score1);
1871 checkScores(score1 + score2, score1 + score2);
1876 checkScores(score1 + score2, score1 + score2);
1878 checkScores(score1 + score2, score2);
1882 checkScores(score2, score2);
1886 checkScores(score2, 0);
1895 peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1896 checkScores(score1, 0);
1897 peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1898 checkScores(score1 + score2, 0);
1901 checkScores(score1 + score2, score1);
1904 checkScores(score1 + score2, score1 + score2);
1907 checkScores(score1, score1);
1917 struct ProofComparatorById {
1922 using ProofSetById = std::set<ProofRef, ProofComparatorById>;
1924 ProofSetById expectedProofs;
1926 auto matchExpectedContent = [&](
const auto &tree) {
1927 auto it = expectedProofs.
begin();
1928 return tree.forEachLeaf([&](
auto pLeaf) {
1929 return it != expectedProofs.end() &&
1930 pLeaf->getId() == (*it++)->getId();
1935 const int64_t sequence = 10;
1940 for (
size_t i = 0; i < 10; i++) {
1941 auto outpoint = createUtxo(active_chainstate, key);
1942 auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1944 expectedProofs.insert(std::move(proof));
1954 ProofSetById addedProofs;
1955 std::vector<COutPoint> outpointsToSpend;
1956 for (
size_t i = 0; i < 10; i++) {
1957 auto outpoint = createUtxo(active_chainstate, key);
1958 auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1960 addedProofs.insert(std::move(proof));
1961 outpointsToSpend.push_back(std::move(outpoint));
1968 expectedProofs.
insert(addedProofs.begin(), addedProofs.end());
1975 for (
const auto &outpoint : outpointsToSpend) {
1987 for (
const auto &proof : addedProofs) {
1993 std::vector<ProofRef> conflictingProofs;
1994 std::vector<COutPoint> conflictingOutpoints;
1995 for (
size_t i = 0; i < 10; i++) {
1996 auto outpoint = createUtxo(active_chainstate, key);
1997 auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1999 conflictingProofs.push_back(std::move(proof));
2000 conflictingOutpoints.push_back(std::move(outpoint));
2004 expectedProofs.
insert(conflictingProofs.begin(), conflictingProofs.end());
2008 for (
size_t i = 0; i < 10; i += 2) {
2011 key, {{conflictingOutpoints[i]}}, sequence - 1)));
2014 auto replacementProof = buildProofWithSequence(
2015 key, {{conflictingOutpoints[i + 1]}}, sequence + 1);
2018 BOOST_CHECK(expectedProofs.insert(replacementProof).second);
2032 auto addNode = [&](
NodeId nodeid) {
2040 for (
NodeId nodeid = 0; nodeid < 10; nodeid++) {
2057 const auto now = GetTime<std::chrono::seconds>();
2058 auto mocktime = now;
2060 auto elapseTime = [&](std::chrono::seconds seconds) {
2061 mocktime += seconds;
2068 const size_t numProofs = 10;
2070 std::vector<COutPoint> outpoints(numProofs);
2071 std::vector<ProofRef> proofs(numProofs);
2072 std::vector<ProofRef> conflictingProofs(numProofs);
2073 for (
size_t i = 0; i < numProofs; i++) {
2075 proofs[i] = buildProofWithSequence(key, {outpoints[i]}, 2);
2076 conflictingProofs[i] = buildProofWithSequence(key, {outpoints[i]}, 1);
2091 return peer.node_count;
2099 TestPeerManager::cleanupDanglingProofs(pm);
2100 for (
size_t i = 0; i < numProofs; i++) {
2107 TestPeerManager::cleanupDanglingProofs(pm);
2108 for (
size_t i = 0; i < numProofs; i++) {
2109 const bool hasNodeAttached = i % 2;
2127 conflictingProofs[0]->getId(),
2132 TestPeerManager::cleanupDanglingProofs(pm);
2133 for (
size_t i = 0; i < numProofs; i++) {
2134 const bool hasNodeAttached = i % 2;
2144 hasNodeAttached || i == 0);
2152 for (
size_t i = 1; i < numProofs; i += 2) {
2156 return peer.node_count == 0;
2161 conflictingProofs[0]->getId(),
2164 TestPeerManager::cleanupDanglingProofs(pm);
2165 for (
size_t i = 0; i < numProofs; i++) {
2166 const bool hadNodeAttached = i % 2;
2183 TestPeerManager::cleanupDanglingProofs(pm);
2185 for (
size_t i = 0; i < numProofs; i++) {
2202 BOOST_CHECK(state.GetResult() == ProofRegistrationResult::MISSING_UTXO);
2209 const int64_t tipTime =
2217 100,
false, tipTime + 1);
2219 1, 100,
false, tipTime + 2);
2231 for (int64_t i = 0; i < 6; i++) {
2232 SetMockTime(proofToExpire->getExpirationTime() + i);
2233 CreateAndProcessBlock({}, CScript());
2237 ->GetMedianTimePast(),
2238 proofToExpire->getExpirationTime());
2254 auto buildProofWithAmountAndPayout = [&](
Amount amount,
2255 const CScript &payoutScript) {
2257 COutPoint utxo = createUtxo(active_chainstate, key, amount);
2258 return buildProof(key, {{std::move(utxo), amount}},
2264 std::vector<std::pair<ProofId, CScript>> winners;
2270 auto now = GetTime<std::chrono::seconds>();
2272 prevBlock.
nTime = now.count();
2281 size_t numProofs = 8;
2282 std::vector<ProofRef> proofs;
2283 proofs.reserve(numProofs);
2284 for (
size_t i = 0; i < numProofs; i++) {
2290 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2291 BOOST_CHECK_NE(peerid,
NO_PEER);
2296 proofs.emplace_back(std::move(proof));
2303 prevBlock.
nTime = now.count();
2309 BOOST_CHECK_LE(winners.size(), numProofs);
2312 for (
size_t i = 0; i < numProofs; i++) {
2313 BOOST_CHECK(TestPeerManager::isFlaky(pm, proofs[i]->getId()));
2315 BOOST_CHECK_LE(winners.size(), numProofs);
2320 BOOST_CHECK(!TestPeerManager::isFlaky(pm, proofs[i]->getId()));
2322 BOOST_CHECK_LE(winners.size(), numProofs - i);
2327 BOOST_CHECK_LE(winners.size(), 1);
2333 const size_t loop_iters =
2334 size_t(-1.0 * std::log(100000.0) /
2335 std::log((
double(numProofs) - 1) / numProofs)) +
2337 BOOST_CHECK_GT(loop_iters, numProofs);
2338 std::unordered_map<std::string, size_t> winningCounts;
2339 for (
size_t i = 0; i < loop_iters; i++) {
2350 for (
size_t i = 0; i < numProofs; i++) {
2351 for (
size_t j = 0; j < numProofs; j++) {
2359 BOOST_CHECK_GT(3. / numProofs, 0.3);
2360 for (
size_t i = 0; i < numProofs; i++) {
2364 proofs[(i - 1 + numProofs) % numProofs]->getId(), nodeid,
false));
2366 proofs[(i + numProofs) % numProofs]->getId(), nodeid,
false));
2368 proofs[(i + 1 + numProofs) % numProofs]->getId(), nodeid,
false));
2373 for (
const auto &proof : proofs) {
2379 for (
const auto &proof : proofs) {
2380 for (
NodeId nodeid = 0; nodeid <
NodeId(numProofs); nodeid++) {
2389 for (
size_t numWinner = 1; numWinner < 4; numWinner++) {
2391 CScript lastWinner = winners[numWinner - 1].second;
2395 for (
const auto &proof : proofs) {
2397 winnerProofId = proof->
getId();
2403 for (
NodeId nodeid = 0; nodeid <
NodeId(numProofs); nodeid++) {
2406 BOOST_CHECK(TestPeerManager::isFlaky(pm, winnerProofId));
2416 CScript lastWinner = winners[3].second;
2419 for (
const auto &proof : proofs) {
2421 winnerProofId = proof->
getId();
2427 for (
NodeId nodeid = 0; nodeid <
NodeId(numProofs); nodeid++) {
2436 for (
auto &proof : proofs) {
2451 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2452 BOOST_CHECK_NE(peerid,
NO_PEER);
2478 prevBlock.
nTime = now.count();
2493 for (
size_t i = 0; i < 4; i++) {
2502 PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2503 BOOST_CHECK_NE(peerid,
NO_PEER);
2505 return peer.registration_time == now + i * 30min;
2513 proofs.push_back(proof);
2518 prevBlock.
nTime = now.count();
2526 prevBlock.
nTime = now.count();
2529 auto checkRegistrationTime =
2530 [&](
const std::pair<ProofId, CScript> &winner) {
2534 (now - 60min).count());
2545 prevBlock.
nTime = now.count();
2548 checkRegistrationTime(winners[0]);
2556 prevBlock.
nTime = now.count();
2558 BOOST_CHECK_LE(winners.size(), 3);
2559 checkRegistrationTime(winners[0]);
2567 prevBlock.
nTime = now.count();
2569 BOOST_CHECK_LE(winners.size(), 3);
2570 checkRegistrationTime(winners[0]);
2577 prevBlock.
nTime = now.count();
2579 BOOST_CHECK_LE(winners.size(), 2);
2580 checkRegistrationTime(winners[0]);
2586 prevBlock.
nTime = now.count();
2589 checkRegistrationTime(winners[0]);
2597 auto mockTime = GetTime<std::chrono::seconds>();
2605 auto checkRemoteProof =
2607 const bool expectedPresent,
2608 const std::chrono::seconds &expectedlastUpdate) {
2612 TestPeerManager::getRemoteProof(pm, proofid, nodeid);
2618 expectedlastUpdate.count());
2676 checkRemoteProof(proofid, 0,
true, mockTime);
2692 checkRemoteProof(proofid, 0,
true, mockTime);
2702 std::vector<ProofRef> proofs;
2707 proofs.push_back(proof);
2720 checkRemoteProof(proofid, 0,
true, mockTime);
2750 auto mockTime = GetTime<std::chrono::seconds>();
2759 for (
NodeId nodeid = 0; nodeid < 12; nodeid++) {
2773 for (
NodeId nodeid = 0; nodeid < 5; nodeid++) {
2776 for (
NodeId nodeid = 5; nodeid < 12; nodeid++) {
2786 TestPeerManager::setLocalProof(pm, localProof);
2794 TestPeerManager::setLocalProof(pm,
ProofRef());
2800 for (
NodeId nodeid = 0; nodeid < 5; nodeid++) {
2803 for (
NodeId nodeid = 5; nodeid < 12; nodeid++) {
2819 for (
NodeId nodeid = 0; nodeid < 5; nodeid++) {
2822 for (
NodeId nodeid = 5; nodeid < 12; nodeid++) {
2830 for (
NodeId nodeid = 0; nodeid < 5; nodeid++) {
2833 for (
NodeId nodeid = 5; nodeid < 12; nodeid++) {
2840 TestPeerManager::clearPeers(pm);
2851 for (
NodeId nodeid = 1; nodeid < 6; nodeid++) {
2858 for (
NodeId nodeid = 1; nodeid < 6; nodeid++) {
2875 auto mockTime = GetTime<std::chrono::seconds>();
2879 std::vector<ProofRef> proofs;
2880 for (
size_t i = 0; i < 10; i++) {
2883 proofs.push_back(proof);
2887 TestPeerManager::cleanupDanglingProofs(pm);
2888 for (
const auto &proof : proofs) {
2898 TestPeerManager::cleanupDanglingProofs(pm);
2899 for (
const auto &proof : proofs) {
2905 for (
NodeId nodeid = 0; nodeid < 10; nodeid++) {
2912 for (
const auto &proof : proofs) {
2918 for (
const auto &proof : proofs) {
2924 std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
2925 TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2926 for (
const auto &proof : proofs) {
2934 for (
NodeId nodeid = 0; nodeid < 10; nodeid++) {
2935 for (
const auto &proof : proofs) {
2941 for (
const auto &proof : proofs) {
2943 !TestPeerManager::getRemotePresenceStatus(pm, proof->
getId())
2948 TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2950 for (
const auto &proof : proofs) {
2959 TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2961 for (
const auto &proof : proofs) {
2967 for (
NodeId nodeid = 0; nodeid < 10; nodeid++) {
2968 for (
const auto &proof : proofs) {
2973 TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2974 for (
const auto &proof : proofs) {
2987 auto mockTime = GetTime<std::chrono::seconds>();
2990 std::vector<ProofRef> proofs;
2991 for (
size_t i = 0; i < 10; i++) {
2998 auto peerid = TestPeerManager::getPeerIdForProofId(pm, proof->getId());
3002 peerid, mockTime + std::chrono::seconds{100 + i}));
3009 proofs.push_back(proof);
3014 const fs::path testDumpPath =
"test_avapeers_dump.dat";
3017 TestPeerManager::clearPeers(pm);
3019 std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
3023 auto findProofIndex = [&proofs](
const ProofId &proofid) {
3024 for (
size_t i = 0; i < proofs.size(); i++) {
3025 if (proofs[i]->getId() == proofid) {
3035 for (
const auto &proof : registeredProofs) {
3037 size_t i = findProofIndex(proofid);
3039 BOOST_CHECK_EQUAL(peer.hasFinalized, i < 5);
3040 BOOST_CHECK_EQUAL(peer.registration_time.count(),
3041 (mockTime + std::chrono::seconds{i}).count());
3043 peer.nextPossibleConflictTime.count(),
3044 (mockTime + std::chrono::seconds{100 + i}).count());
3050 TestPeerManager::clearPeers(pm);
3063 registeredProofs.insert(proofs[0]);
3073 file << static_cast<uint64_t>(-1);
3074 file << uint64_t{0};
3079 registeredProofs.insert(proofs[0]);
3090 const uint64_t now =
GetTime();
3092 file << static_cast<uint64_t>(1);
3093 file << uint64_t{2};
3110 proofs[0]->getId());
3122 auto utxo = createUtxo(active_chainstate, key);
3125 GetTime<std::chrono::seconds>().count() + 1000000);
3136 TestPeerManager::cleanupDanglingProofs(pm);
3174 TestPeerManager::cleanupDanglingProofs(pm);
3180 for (int64_t i = 0; i < 6; i++) {
3182 CreateAndProcessBlock({}, CScript());
3186 ->GetMedianTimePast(),
3196BOOST_AUTO_TEST_SUITE_END()
static constexpr PeerId NO_PEER
#define Assert(val)
Identity function.
void ForceSetArg(const std::string &strArg, const std::string &strValue)
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Non-refcounted RAII wrapper for FILE*.
The block chain is a tree shaped structure starting with the genesis block at the root,...
const BlockHash * phashBlock
pointer to the hash of the block, if any.
CCoinsView that adds a memory cache for transactions to another CCoinsView.
void AddCoin(const COutPoint &outpoint, Coin coin, bool possible_overwrite)
Add a coin.
bool SpendCoin(const COutPoint &outpoint, Coin *moveto=nullptr)
Spend a coin.
An encapsulated secp256k1 private key.
static CKey MakeCompressedKey()
Produce a valid compressed key.
CPubKey GetPubKey() const
Compute the public key from a private key.
An output of a transaction.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
bool InvalidateBlock(BlockValidationState &state, CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex
Mark a block as invalid.
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(const CBlockIndex *GetSnapshotBaseBlock() const EXCLUSIVE_LOCKS_REQUIRED(Chainstate ActiveChainstate)() const
Once the background validation chainstate has reached the height which is the base of the UTXO snapsh...
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
bool selectStakingRewardWinner(const CBlockIndex *pprev, std::vector< std::pair< ProofId, CScript > > &winners)
Deterministically select a list of payout scripts based on the proof set and the previous block hash.
bool removeNode(NodeId nodeid)
bool setFinalized(PeerId peerid)
Latch on that this peer has a finalized proof.
bool dumpPeersToFile(const fs::path &dumpPath) const
RemoteProofSet remoteProofs
Remember which node sent which proof so we have an image of the proof set of our peers.
uint64_t getFragmentation() const
uint32_t getConnectedPeersScore() const
bool updateNextRequestTimeForResponse(NodeId nodeid, const Response &response)
bool isDangling(const ProofId &proofid) const
bool addNode(NodeId nodeid, const ProofId &proofid, size_t max_elements)
Node API.
std::optional< bool > getRemotePresenceStatus(const ProofId &proofid) const
Get the presence remote status of a proof.
bool shouldRequestMoreNodes()
Returns true if we encountered a lack of node since the last call.
bool exists(const ProofId &proofid) const
Return true if the (valid) proof exists, but only for non-dangling proofs.
size_t getNodeCount() const
PendingNodeSet pendingNodes
bool verify() const
Perform consistency check on internal data structures.
bool forNode(NodeId nodeid, Callable &&func) const
bool hasRemoteProofStatus(const ProofId &proofid) const
bool forPeer(const ProofId &proofid, Callable &&func) const
uint32_t getTotalPeersScore() const
bool latchAvaproofsSent(NodeId nodeid)
Flag that a node did send its compact proofs.
bool updateNextRequestTimeForPoll(NodeId nodeid, SteadyMilliseconds timeout, uint64_t round)
uint64_t getSlotCount() const
bool loadPeersFromFile(const fs::path &dumpPath, std::unordered_set< ProofRef, SaltedProofHasher > ®isteredProofs)
std::unordered_set< ProofRef, SaltedProofHasher > updatedBlockTip()
Update the peer set when a new block is connected.
const ProofRadixTree & getShareableProofsSnapshot() const
bool isBoundToPeer(const ProofId &proofid) const
size_t getPendingNodeCount() const
bool saveRemoteProof(const ProofId &proofid, const NodeId nodeid, const bool present)
uint64_t compact()
Trigger maintenance of internal data structures.
std::vector< Slot > slots
void forEachPeer(Callable &&func) const
bool isFlaky(const ProofId &proofid) const
bool removePeer(const PeerId peerid)
Remove an existing peer.
bool isImmature(const ProofId &proofid) const
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
RegistrationMode
Registration mode.
static constexpr size_t MAX_REMOTE_PROOFS
PeerId selectPeer() const
Randomly select a peer to poll.
bool isInConflictingPool(const ProofId &proofid) const
bool isRemotelyPresentProof(const ProofId &proofid) const
void cleanupDanglingProofs(std::unordered_set< ProofRef, SaltedProofHasher > ®isteredProofs)
ProofRef getProof(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState ®istrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
bool updateNextPossibleConflictTime(PeerId peerid, const std::chrono::seconds &nextTime)
Proof and Peer related API.
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
int64_t getExpirationTime() const
const CScript & getPayoutScript() const
const ProofId & getId() const
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
static const uint256 ZERO
static void addCoin(const Amount nValue, const CWallet &wallet, std::vector< std::unique_ptr< CWalletTx > > &wtxs)
std::string FormatScript(const CScript &script)
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
static RPCHelpMan generate()
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
static constexpr uint32_t AVALANCHE_MAX_IMMATURE_PROOFS
Maximum number of immature proofs the peer manager will accept from the network.
const CScript UNSPENDABLE_ECREG_PAYOUT_SCRIPT
ProofRef buildRandomProof(Chainstate &active_chainstate, uint32_t score, int height, const CKey &masterKey)
constexpr uint32_t MIN_VALID_PROOF_SCORE
PeerId selectPeerImpl(const std::vector< Slot > &slots, const uint64_t slot, const uint64_t max)
Internal methods that are exposed for testing purposes.
RCUPtr< const Proof > ProofRef
FILE * fopen(const fs::path &p, const char *mode)
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
static void addNodeWithScore(Chainstate &active_chainstate, avalanche::PeerManager &pm, NodeId node, uint32_t score)
BOOST_AUTO_TEST_CASE(select_peer_linear)
BOOST_FIXTURE_TEST_CASE(conflicting_proof_rescan, NoCoolDownFixture)
static constexpr size_t DEFAULT_AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
uint256 GetRandHash() noexcept
========== CONVENIENCE FUNCTIONS FOR COMMONLY USED RANDOMNESS ==========
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
A BlockHash is a unqiue identifier for a block.
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
A TxId is the identifier of a transaction.
Compare conflicting proofs.
std::chrono::seconds registration_time
static constexpr auto DANGLING_TIMEOUT
Consider dropping the peer if no node is attached after this timeout expired.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
std::chrono::time_point< std::chrono::steady_clock, std::chrono::milliseconds > SteadyMilliseconds