Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/peermanager_tests.cpp
Show First 20 Lines • Show All 1,145 Lines • ▼ Show 20 Lines | auto proof_seq30 = buildProofWithSequenceAndOutpoints( | ||||
BOOST_CHECK(!pm.registerProof(proof_seq30)); | BOOST_CHECK(!pm.registerProof(proof_seq30)); | ||||
BOOST_CHECK(pm.isBoundToPeer(proof_seq10->getId())); | BOOST_CHECK(pm.isBoundToPeer(proof_seq10->getId())); | ||||
BOOST_CHECK(pm.isInConflictingPool(proof_seq30->getId())); | BOOST_CHECK(pm.isInConflictingPool(proof_seq30->getId())); | ||||
} | } | ||||
gArgs.ClearForcedArg("-avalancheconflictingproofcooldown"); | gArgs.ClearForcedArg("-avalancheconflictingproofcooldown"); | ||||
} | } | ||||
BOOST_FIXTURE_TEST_CASE(accept_reject_conflicting_proof, | |||||
NoConflictingCooldownFixture) { | |||||
avalanche::PeerManager pm; | |||||
const CKey key = CKey::MakeCompressedKey(); | |||||
uint64_t sequence = 42; | |||||
const int64_t expiration = 0; | |||||
const Amount amount = 10 * COIN; | |||||
const uint32_t height = 100; | |||||
const bool is_coinbase = false; | |||||
CScript script = GetScriptForDestination(PKHash(key.GetPubKey())); | |||||
auto addCoin = [&]() { | |||||
LOCK(cs_main); | |||||
COutPoint outpoint(TxId(GetRandHash()), 0); | |||||
CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); | |||||
coins.AddCoin(outpoint, | |||||
Coin(CTxOut(amount, script), height, is_coinbase), false); | |||||
return outpoint; | |||||
}; | |||||
gArgs.ForceSetArg("-enableavalancheproofreplacement", "1"); | |||||
// Create a bunch of proofs with a single utxo, and remember these utxos so | |||||
// we can create conflicting proofs | |||||
std::vector<COutPoint> conflictingOutpoints; | |||||
std::set<ProofRef> conflictingProofs; | |||||
for (size_t i = 0; i < 10; i++) { | |||||
COutPoint outpoint = addCoin(); | |||||
conflictingOutpoints.push_back(outpoint); | |||||
ProofBuilder pb(sequence, expiration, key); | |||||
BOOST_CHECK( | |||||
pb.addUTXO(std::move(outpoint), amount, height, is_coinbase, key)); | |||||
auto proof = pb.build(); | |||||
BOOST_CHECK(pm.registerProof(proof)); | |||||
conflictingProofs.insert(std::move(proof)); | |||||
}; | |||||
// Add a few more proofs for good measure | |||||
for (size_t i = 0; i < 5; i++) { | |||||
ProofBuilder pb(0, 0, key); | |||||
BOOST_CHECK(pb.addUTXO(addCoin(), amount, height, is_coinbase, key)); | |||||
BOOST_CHECK(pm.registerProof(pb.build())); | |||||
}; | |||||
auto buildConflictingProofWithSequence = [&](uint64_t proofSequence) { | |||||
// Create a proof that conflicts with all the conflicting outpoints | |||||
ProofBuilder pb(proofSequence, expiration, key); | |||||
for (const COutPoint &outpoint : conflictingOutpoints) { | |||||
BOOST_CHECK(pb.addUTXO(outpoint, amount, height, is_coinbase, key)); | |||||
} | |||||
// Add a few other utxos for good measure | |||||
for (size_t i = 0; i < 5; i++) { | |||||
BOOST_CHECK( | |||||
pb.addUTXO(addCoin(), amount, height, is_coinbase, key)); | |||||
}; | |||||
return pb.build(); | |||||
}; | |||||
auto lowSeqProof = buildConflictingProofWithSequence(sequence - 1); | |||||
// It has conflicts and is not the best candidate | |||||
BOOST_CHECK(!pm.registerProof(lowSeqProof)); | |||||
BOOST_CHECK(pm.isInConflictingPool(lowSeqProof->getId())); | |||||
auto checkAcceptance = [&](const ProofRef &proof) { | |||||
BOOST_CHECK(pm.acceptProof(proof)); | |||||
BOOST_CHECK(pm.isBoundToPeer(proof->getId())); | |||||
for (const ProofRef &p : conflictingProofs) { | |||||
BOOST_CHECK(pm.isInConflictingPool(p->getId())); | |||||
} | |||||
}; | |||||
auto checkRejection = [&](const ProofRef &proof) { | |||||
BOOST_CHECK(pm.rejectProof(proof)); | |||||
BOOST_CHECK(pm.isInConflictingPool(proof->getId())); | |||||
for (const ProofRef &p : conflictingProofs) { | |||||
BOOST_CHECK(pm.isBoundToPeer(p->getId())); | |||||
} | |||||
}; | |||||
auto checkRescan = [&](const ProofRef &proof, bool expectValid) { | |||||
pm.updatedBlockTip(); | |||||
const ProofId &proofid = proof->getId(); | |||||
BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofid), expectValid); | |||||
BOOST_CHECK_EQUAL(pm.isInConflictingPool(proofid), !expectValid); | |||||
for (const ProofRef &p : conflictingProofs) { | |||||
BOOST_CHECK_EQUAL(pm.isBoundToPeer(p->getId()), !expectValid); | |||||
BOOST_CHECK_EQUAL(pm.isInConflictingPool(p->getId()), expectValid); | |||||
} | |||||
}; | |||||
// Accept the proof | |||||
checkAcceptance(lowSeqProof); | |||||
// Accepting a few more times has no effect | |||||
for (size_t i = 0; i < 10; i++) { | |||||
checkAcceptance(lowSeqProof); | |||||
} | |||||
// Make sure that a rescan will not change the state | |||||
checkRescan(lowSeqProof, true); | |||||
// Reject the proof | |||||
checkRejection(lowSeqProof); | |||||
// Rejecting a few more times has no effect | |||||
for (size_t i = 0; i < 10; i++) { | |||||
checkRejection(lowSeqProof); | |||||
} | |||||
checkRescan(lowSeqProof, false); | |||||
// Finally accept the proof | |||||
checkAcceptance(lowSeqProof); | |||||
auto highSeqProof = buildConflictingProofWithSequence(sequence + 1); | |||||
BOOST_CHECK(pm.registerProof(highSeqProof)); | |||||
BOOST_CHECK(pm.isBoundToPeer(highSeqProof->getId())); | |||||
// The lowSeqProof is now evicted, because the conflicting proofs are better | |||||
// candidates. | |||||
BOOST_CHECK(!pm.exists(lowSeqProof->getId())); | |||||
// Reject the proof | |||||
checkRejection(highSeqProof); | |||||
// Rejecting a few more times has no effect | |||||
for (size_t i = 0; i < 10; i++) { | |||||
checkRejection(highSeqProof); | |||||
} | |||||
checkRescan(highSeqProof, false); | |||||
// Accept the proof | |||||
checkAcceptance(highSeqProof); | |||||
// Accepting a few more times has no effect | |||||
for (size_t i = 0; i < 10; i++) { | |||||
checkAcceptance(highSeqProof); | |||||
} | |||||
checkRescan(highSeqProof, true); | |||||
// Sanity check that nothing is broken internally | |||||
BOOST_CHECK(pm.verify()); | |||||
gArgs.ClearForcedArg("-enableavalancheproofreplacement"); | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |