diff --git a/src/avalanche/avalanche.h b/src/avalanche/avalanche.h --- a/src/avalanche/avalanche.h +++ b/src/avalanche/avalanche.h @@ -24,6 +24,12 @@ */ static constexpr bool AVALANCHE_DEFAULT_PEER_DISCOVERY_ENABLED = false; +/** + * Conflicting proofs cooldown time default value in seconds. + * Minimal delay between two proofs with at least a common UTXO. + */ +static constexpr size_t AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN = 60; + /** * Avalanche default cooldown in milliseconds. */ diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -78,10 +78,11 @@ std::chrono::seconds registration_time; std::chrono::seconds nextPossibleConflictTime; - Peer(PeerId peerid_, ProofRef proof_) + Peer(PeerId peerid_, ProofRef proof_, + std::chrono::seconds nextPossibleConflictTime_) : peerid(peerid_), proof(std::move(proof_)), registration_time(GetTime()), - nextPossibleConflictTime(registration_time) {} + nextPossibleConflictTime(std::move(nextPossibleConflictTime_)) {} const ProofId &getProofId() const { return proof->getId(); } uint32_t getScore() const { return proof->getScore(); } @@ -112,6 +113,7 @@ INVALID, CONFLICTING, REJECTED, + COOLDOWN_NOT_ELAPSED, }; class ProofRegistrationState : public ValidationState { diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -204,10 +205,37 @@ return invalidate(ProofRegistrationResult::INVALID, "invalid-proof"); } + auto now = GetTime(); + auto nextCooldownTimePoint = + now + std::chrono::seconds( + gArgs.GetArg("-avalancheconflictingproofcooldown", + AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN)); + ProofPool::ConflictingProofSet conflictingProofs; switch (validProofPool.addProofIfNoConflict(proof, conflictingProofs)) { case ProofPool::AddProofStatus::REJECTED: { if (mode != RegistrationMode::FORCE_ACCEPT) { + auto bestPossibleConflictTime = std::chrono::seconds(); + auto &pview = peers.get(); + for (auto &conflictingProof : conflictingProofs) { + auto it = pview.find(conflictingProof->getId()); + assert(it != pview.end()); + + // Search the most recent time over the peers + bestPossibleConflictTime = std::max( + bestPossibleConflictTime, it->nextPossibleConflictTime); + + updateNextPossibleConflictTime(it->peerid, + nextCooldownTimePoint); + } + + if (bestPossibleConflictTime > now) { + // Cooldown not elapsed, reject the proof. + return invalidate( + ProofRegistrationResult::COOLDOWN_NOT_ELAPSED, + "cooldown-not-elapsed"); + } + // The proof has conflicts, move it to the conflicting proof // pool so it can be pulled back if the conflicting ones are // invalidated. @@ -256,7 +284,7 @@ const PeerId peerid = nextPeerId++; // We have no peer for this proof, time to create it. - auto inserted = peers.emplace(peerid, proof); + auto inserted = peers.emplace(peerid, proof, nextCooldownTimePoint); assert(inserted.second); // If there are nodes waiting for this proof, add them 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 @@ -8,6 +8,7 @@ #include #include #include