diff --git a/src/avalanche/proofpool.h b/src/avalanche/proofpool.h new file mode 100644 --- /dev/null +++ b/src/avalanche/proofpool.h @@ -0,0 +1,104 @@ +// 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_PROOFPOOL_H +#define BITCOIN_AVALANCHE_PROOFPOOL_H + +#include +#include + +#include +#include +#include + +namespace avalanche { + +/** + * In a worst case scenario, if all proofs have 1000 utxos, 500 proofs + * amounts to about 100 MB of memory. + */ +static constexpr size_t PROOF_POOL_MAX_SIZE = 500; + +class SaltedProofIdHasher : private SaltedUint256Hasher { +public: + SaltedProofIdHasher() : SaltedUint256Hasher() {} + size_t operator()(const ProofId &proofid) const { return hash(proofid); } +}; + +// Extracts a ProofId from a Proof +struct proofid_extractor { + typedef ProofId result_type; + result_type operator()(const Proof &proof) const { return proof.getId(); } +}; + +struct by_sequence {}; +struct by_proofid {}; + +typedef 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>>> + proofContainer; + +/** + * ProofPool stores orphan proofs waiting for their UTXOs to be found. + * + * When the pool is full, the oldest proof is removed before a new one is + * added. + */ +class ProofPool { + proofContainer proofs; + + /** + * Trim the proof pool to given max size. + * It the current size is <= max size this has no effect. + * @param max_size The maximum size of the proof pool + * @return bool True on success + */ + bool trimToSize(size_t max_size) { + assert(max_size > 0); + auto &proofs_by_sequence = proofs.get(); + if (max_size >= proofs_by_sequence.size()) { + return true; + } + proofs_by_sequence.resize(max_size); + return proofs_by_sequence.size() <= max_size; + } + +public: + /** + * Add a proof to the pool. + * The caller is responsible for checking the proof. + */ + bool addProof(const Proof &proof) { + return trimToSize(PROOF_POOL_MAX_SIZE - 1) && + proofs.get().push_front(proof).second; + } + + /** Remove a proof from the pool. */ + bool removeProof(const ProofId &proofId) { + return proofs.get().erase(proofId) == 1; + } + + /** + * Get a pointer to a proof by id, or nullptr if the proof is not in the + * pool. + */ + const Proof *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; + } + + // For testing + size_t getSize() const { return proofs.get().size(); } +}; + +} // namespace avalanche + +#endif // BITCOIN_AVALANCHE_PROOFPOOL_H \ No newline at end of file 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 @@ -17,6 +17,7 @@ peermanager_tests.cpp processor_tests.cpp proof_tests.cpp + proofpool_tests.cpp ) target_link_libraries(test-avalanche server testutil) diff --git a/src/avalanche/test/proofpool_tests.cpp b/src/avalanche/test/proofpool_tests.cpp new file mode 100644 --- /dev/null +++ b/src/avalanche/test/proofpool_tests.cpp @@ -0,0 +1,94 @@ +// 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