diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -539,6 +539,7 @@ addrman.cpp avalanche/delegation.cpp avalanche/delegationbuilder.cpp + avalanche/orphanproofpool.cpp avalanche/peermanager.cpp avalanche/processor.cpp avalanche/proof.cpp diff --git a/src/avalanche/orphanproofpool.h b/src/avalanche/orphanproofpool.h new file mode 100644 --- /dev/null +++ b/src/avalanche/orphanproofpool.h @@ -0,0 +1,78 @@ +// Copyright (c) 2021 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_AVALANCHE_ORPHANPROOFPOOL_H +#define BITCOIN_AVALANCHE_ORPHANPROOFPOOL_H + +#include + +#include +#include +#include + +namespace avalanche { + +// Extracts a ProofId from a Proof +struct proofid_extractor { + using result_type = ProofId; + result_type operator()(const Proof &proof) const { return proof.getId(); } +}; + +struct by_sequence {}; +struct by_proofid {}; + +using ProofContainer = boost::multi_index_container< + Proof, + boost::multi_index::indexed_by< + // keep insertion order + boost::multi_index::sequenced>, + // indexed by proofid + boost::multi_index::hashed_unique, + proofid_extractor, + SaltedProofIdHasher>>>; + +/** + * OrphanProofPool stores orphan proofs waiting for their UTXOs to be + * discovered. + * The pool has a size limit. When the pool is full, the oldest proof is + * removed when a new one is added. + */ +class OrphanProofPool { + ProofContainer proofs; + + const size_t maxNumberOfStakes; + size_t nStakes = 0; + + /** + * Trim the proof pool to given max size. + * It the current size is <= max size this has no effect. + */ + void trimToMaximumSize() noexcept; + +public: + OrphanProofPool(size_t maxNumberOfStakes) + : maxNumberOfStakes(maxNumberOfStakes) {} + + /** + * Add a proof to the pool. + * The caller is responsible for checking the proof. + */ + bool addProof(Proof proof); + + /** Remove a proof from the pool. */ + bool removeProof(const ProofId &proofId); + + /** + * Get a pointer to a proof by id, or nullptr if the proof is not in the + * pool. + */ + const Proof *getProof(ProofId proofId) const; + + // For testing + size_t getNProofs() const; + size_t getNStakes() const; +}; + +} // namespace avalanche + +#endif // BITCOIN_AVALANCHE_ORPHANPROOFPOOL_H diff --git a/src/avalanche/orphanproofpool.cpp b/src/avalanche/orphanproofpool.cpp new file mode 100644 --- /dev/null +++ b/src/avalanche/orphanproofpool.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2021 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +namespace avalanche { + +void OrphanProofPool::trimToMaximumSize() noexcept { + auto &proofs_by_sequence = proofs.get(); + auto it = proofs_by_sequence.begin(); + while (nStakes > maxNumberOfStakes) { + nStakes -= it->getStakes().size(); + it = proofs_by_sequence.erase(it); + } +} + +bool OrphanProofPool::addProof(Proof proof) { + if (!proofs.push_back(proof).second) { + return false; + } + nStakes += proof.getStakes().size(); + trimToMaximumSize(); + return true; +} + +bool OrphanProofPool::removeProof(const ProofId &proofId) { + auto &proofs_by_id = proofs.get(); + auto it = proofs_by_id.find(proofId); + if (it == proofs_by_id.end()) { + return false; + } + nStakes -= it->getStakes().size(); + proofs_by_id.erase(it); + return true; +} + +const Proof *OrphanProofPool::getProof(ProofId proofId) const { + auto &proofs_by_proofid = proofs.get(); + auto it = proofs_by_proofid.find(proofId); + return it == proofs_by_proofid.end() ? nullptr : &*it; +} + +size_t OrphanProofPool::getNProofs() const { + return proofs.size(); +} + +size_t OrphanProofPool::getNStakes() const { + return nStakes; +} + +} // namespace avalanche diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -77,13 +77,6 @@ result_type operator()(const Peer &p) const { return p.proof.getId(); } }; -class SaltedProofIdHasher : private SaltedUint256Hasher { -public: - SaltedProofIdHasher() : SaltedUint256Hasher() {} - - size_t operator()(const ProofId &proofid) const { return hash(proofid); } -}; - struct next_request_time {}; class PeerManager { diff --git a/src/avalanche/proofid.h b/src/avalanche/proofid.h --- a/src/avalanche/proofid.h +++ b/src/avalanche/proofid.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_AVALANCHE_PROOFID_H #define BITCOIN_AVALANCHE_PROOFID_H +#include #include #include @@ -21,6 +22,13 @@ return r; } }; + +class SaltedProofIdHasher : private SaltedUint256Hasher { +public: + SaltedProofIdHasher() : SaltedUint256Hasher() {} + size_t operator()(const ProofId &proofid) const { return hash(proofid); } +}; + } // namespace avalanche #endif // BITCOIN_AVALANCHE_PROOFID_H diff --git a/src/avalanche/test/CMakeLists.txt b/src/avalanche/test/CMakeLists.txt --- a/src/avalanche/test/CMakeLists.txt +++ b/src/avalanche/test/CMakeLists.txt @@ -14,6 +14,7 @@ TESTS delegation_tests.cpp + orphanproofpool_tests.cpp peermanager_tests.cpp processor_tests.cpp proof_tests.cpp diff --git a/src/avalanche/test/orphanproofpool_tests.cpp b/src/avalanche/test/orphanproofpool_tests.cpp new file mode 100644 --- /dev/null +++ b/src/avalanche/test/orphanproofpool_tests.cpp @@ -0,0 +1,181 @@ +// Copyright (c) 2021 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include