diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -986,12 +986,19 @@ Peer::DANGLING_TIMEOUT) .count(); + const int64_t recentRegistrationTime = + std::min(pprev->GetBlockTime(), GetTime()) - + std::chrono::duration_cast(4 * + Peer::DANGLING_TIMEOUT) + .count(); + const BlockHash prevblockhash = pprev->GetBlockHash(); winners.clear(); while (winners.size() < peers.size()) { double bestRewardRank = std::numeric_limits::max(); ProofRef selectedProof = ProofRef(); + int64_t selectedProofRegistrationTime{0}; uint256 bestRewardHash; for (const Peer &peer : peers) { @@ -1042,6 +1049,7 @@ if (proofRewardRank < bestRewardRank) { bestRewardRank = proofRewardRank; selectedProof = peer.proof; + selectedProofRegistrationTime = peer.registration_time.count(); bestRewardHash = proofRewardHash; } @@ -1052,6 +1060,7 @@ (proofRewardHash == bestRewardHash && peer.getProofId() < selectedProof->getId()))) { selectedProof = peer.proof; + selectedProofRegistrationTime = peer.registration_time.count(); bestRewardHash = proofRewardHash; } } @@ -1063,7 +1072,8 @@ winners.push_back(selectedProof->getPayoutScript()); - if (!isFlaky(selectedProof->getId())) { + if (selectedProofRegistrationTime < recentRegistrationTime && + !isFlaky(selectedProof->getId())) { break; } } diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp --- a/src/avalanche/test/peermanager_tests.cpp +++ b/src/avalanche/test/peermanager_tests.cpp @@ -2348,8 +2348,8 @@ } // Make sure the proofs have been registered before the prev block was found - // and before 2x the peer replacement cooldown. - now += 30min + 1s; + // and before 4x the peer replacement cooldown. + now += 4 * avalanche::Peer::DANGLING_TIMEOUT + 1s; SetMockTime(now); prevBlock.nTime = now.count(); @@ -2529,6 +2529,46 @@ BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners)); // With a single proof, it's easy to determine the winner BOOST_CHECK_EQUAL(FormatScript(winners[0]), FormatScript(payoutScript)); + + // Remove the proof + BOOST_CHECK(pm.rejectProof( + proof->getId(), avalanche::PeerManager::RejectionMode::INVALIDATE)); + } + + { + for (size_t i = 0; i < 2; i++) { + // Add a couple proofs + const CKey key = CKey::MakeCompressedKey(); + CScript payoutScript = GetScriptForRawPubKey(key.GetPubKey()); + + auto proof = buildProofWithAmountAndPayout(PROOF_DUST_THRESHOLD, + payoutScript); + PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof); + BOOST_CHECK_NE(peerid, NO_PEER); + + BOOST_CHECK(pm.addNode(NodeId(i), proof->getId())); + + BOOST_CHECK(pm.setFinalized(peerid)); + } + + // The proofs has been registered > 30min from the previous block time, + // but less than 60min + now += 30min + 1s; + SetMockTime(now); + prevBlock.nTime = now.count(); + + // Because they are both recently registered, both proofs are acceptable + BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners)); + BOOST_CHECK_EQUAL(winners.size(), 2); + + // The proofs has been registered > 60min from the previous block time + now += 30min; + SetMockTime(now); + prevBlock.nTime = now.count(); + + // Now only one is acceptable + BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners)); + BOOST_CHECK_EQUAL(winners.size(), 1); } }