diff --git a/src/avalanche/avalanche.h b/src/avalanche/avalanche.h --- a/src/avalanche/avalanche.h +++ b/src/avalanche/avalanche.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_AVALANCHE_AVALANCHE_H #define BITCOIN_AVALANCHE_AVALANCHE_H +#include #include #include @@ -67,6 +68,12 @@ */ static constexpr double AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO = 0; +/** + * Avalanche default cooldown in milliseconds. + */ +static constexpr std::chrono::milliseconds AVALANCHE_REJECTED_PROOF_LIFETIME{ + 1000 * 10}; + /** * Global avalanche instance. */ diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -356,6 +356,9 @@ bool isOrphan(const ProofId &proofid) const; bool isInConflictingPool(const ProofId &proofid) const; + std::chrono::milliseconds + getTimeInConflictingPool(const ProofId &proofid) const; + private: template void moveToConflictingPool(const ProofContainer &proofs); diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -476,6 +476,11 @@ return conflictingProofPool.getProof(proofid) != nullptr; } +std::chrono::milliseconds +PeerManager::getTimeInConflictingPool(const ProofId &proofid) const { + return conflictingProofPool.getTimeInPool(proofid); +} + bool PeerManager::removePeer(const PeerId peerid) { auto it = peers.find(peerid); if (it == peers.end()) { diff --git a/src/avalanche/proofpool.h b/src/avalanche/proofpool.h --- a/src/avalanche/proofpool.h +++ b/src/avalanche/proofpool.h @@ -24,13 +24,13 @@ struct ProofPoolEntry { size_t utxoIndex; ProofRef proof; + std::chrono::milliseconds timeAdded; const COutPoint &getUTXO() const { return proof->getStakes().at(utxoIndex).getStake().getUTXO(); } - ProofPoolEntry(size_t _utxoIndex, ProofRef _proof) - : utxoIndex(_utxoIndex), proof(std::move(_proof)) {} + ProofPoolEntry(size_t _utxoIndex, ProofRef _proof); }; struct by_utxo; @@ -103,6 +103,8 @@ ProofRef getProof(const ProofId &proofid) const; ProofRef getProof(const COutPoint &outpoint) const; + std::chrono::milliseconds getTimeInPool(const ProofId &proofid) const; + size_t size() const { return pool.size(); } }; diff --git a/src/avalanche/proofpool.cpp b/src/avalanche/proofpool.cpp --- a/src/avalanche/proofpool.cpp +++ b/src/avalanche/proofpool.cpp @@ -6,9 +6,14 @@ #include #include +#include namespace avalanche { +ProofPoolEntry::ProofPoolEntry(size_t _utxoIndex, ProofRef _proof) + : utxoIndex(_utxoIndex), proof(std::move(_proof)), + timeAdded(GetTime()) {} + ProofPool::AddProofStatus ProofPool::addProofIfNoConflict(const ProofRef &proof, ConflictingProofSet &conflictingProofs) { @@ -99,4 +104,14 @@ return it == pool.end() ? nullptr : it->proof; } +std::chrono::milliseconds +ProofPool::getTimeInPool(const ProofId &proofid) const { + auto &poolView = pool.get(); + auto it = poolView.find(proofid); + auto now = GetTime(); + // TODO need better error handling + return it == poolView.end() ? std::chrono::milliseconds::max() + : now - it->timeAdded; +} + } // namespace avalanche diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5210,6 +5210,16 @@ case avalanche::VoteStatus::Rejected: if (g_avalanche->withPeerManager( [&](avalanche::PeerManager &pm) { + if (pm.isInConflictingPool(proofid) && + pm.getTimeInConflictingPool(proofid) >= + AVALANCHE_REJECTED_PROOF_LIFETIME) { + // If this proof has been in the conflicting + // pool for too long, just remove the proof. + // It's not useful. + rejectionMode = avalanche::PeerManager:: + RejectionMode::INVALIDATE; + } + pm.rejectProof(proofid, rejectionMode); return pm.exists(proofid); })) { @@ -5217,6 +5227,13 @@ "ERROR: Failed to reject proof: %s\n", proofid.GetHex()); } + + if (rejectionMode == + avalanche::PeerManager::RejectionMode::DEFAULT) { + // Voting on this proof was inconclusive. Relay it to + // peers so they might have it in the next round. + RelayProof(proofid, m_connman); + } break; case avalanche::VoteStatus::Finalized: nextCooldownTimePoint += std::chrono::seconds(gArgs.GetArg(