diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -544,6 +544,7 @@ addrdb.cpp addrman.cpp avalanche/avalanche.cpp + avalanche/compactproofs.cpp avalanche/delegation.cpp avalanche/delegationbuilder.cpp avalanche/peermanager.cpp diff --git a/src/avalanche/compactproofs.h b/src/avalanche/compactproofs.h new file mode 100644 --- /dev/null +++ b/src/avalanche/compactproofs.h @@ -0,0 +1,66 @@ +// Copyright (c) 2022 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_COMPACTPROOFS_H +#define BITCOIN_AVALANCHE_COMPACTPROOFS_H + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace avalanche { + +namespace { + struct TestCompactProofs; +} + +struct ProofId; + +class CompactProofs { +private: + uint64_t shortproofidk0, shortproofidk1; + std::vector shortproofids; + +public: + static constexpr int SHORTPROOFIDS_LENGTH = 6; + + CompactProofs() + : shortproofidk0(GetRand(std::numeric_limits::max())), + shortproofidk1(GetRand(std::numeric_limits::max())) {} + CompactProofs(const RadixTree &proofs); + + uint64_t getShortID(const ProofId &proofid) const; + + size_t size() const { return shortproofids.size(); } + std::pair getKeys() const { + return std::make_pair(shortproofidk0, shortproofidk1); + } + + SERIALIZE_METHODS(CompactProofs, obj) { + READWRITE( + obj.shortproofidk0, obj.shortproofidk1, + Using>>( + obj.shortproofids)); + if (ser_action.ForRead()) { + if (obj.size() > std::numeric_limits::max()) { + throw std::ios_base::failure( + "CompactProofs indices overflowed 32 bits"); + } + } + } + +private: + friend struct ::avalanche::TestCompactProofs; +}; + +} // namespace avalanche + +#endif // BITCOIN_AVALANCHE_COMPACTPROOFS_H diff --git a/src/avalanche/compactproofs.cpp b/src/avalanche/compactproofs.cpp new file mode 100644 --- /dev/null +++ b/src/avalanche/compactproofs.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2022 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 +#include + +namespace avalanche { + +CompactProofs::CompactProofs(const RadixTree &proofs) + : CompactProofs() { + proofs.forEachLeaf([&](auto pLeaf) { + shortproofids.push_back(getShortID(pLeaf->proof->getId())); + }); +} + +uint64_t CompactProofs::getShortID(const ProofId &proofid) const { + static_assert(SHORTPROOFIDS_LENGTH == 6, + "shortproofids calculation assumes 6-byte shortproofids"); + return SipHashUint256(shortproofidk0, shortproofidk1, proofid) & + 0xffffffffffffL; +} + +} // namespace avalanche 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 @@ -13,6 +13,7 @@ util.cpp TESTS + compactproofs_tests.cpp delegation_tests.cpp init_tests.cpp peermanager_tests.cpp diff --git a/src/avalanche/test/compactproofs_tests.cpp b/src/avalanche/test/compactproofs_tests.cpp new file mode 100644 --- /dev/null +++ b/src/avalanche/test/compactproofs_tests.cpp @@ -0,0 +1,79 @@ +// 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 + +#include + +#include + +namespace avalanche { +namespace { + struct TestCompactProofs { + static std::vector getShortProofIds(const CompactProofs &cp) { + return cp.shortproofids; + } + }; +} // namespace +} // namespace avalanche + +using namespace avalanche; + +// TestingSetup is required for buildRandomProof() +BOOST_FIXTURE_TEST_SUITE(compactproofs_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(compactproofs_roundtrip) { + { + CompactProofs cpw; + BOOST_CHECK_EQUAL(cpw.size(), 0); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + BOOST_CHECK_NO_THROW(ss << cpw); + + CompactProofs cpr; + BOOST_CHECK_NO_THROW(ss >> cpr); + + BOOST_CHECK_EQUAL(cpr.size(), 0); + BOOST_CHECK_EQUAL(cpr.getKeys().first, cpw.getKeys().first); + BOOST_CHECK_EQUAL(cpr.getKeys().second, cpw.getKeys().second); + } + + { + const size_t N = 1000; + + RadixTree proofs; + for (size_t i = 0; i < N; i++) { + BOOST_CHECK(proofs.insert( + RCUPtr::make( + buildRandomProof(MIN_VALID_PROOF_SCORE)))); + } + + CompactProofs cpw(proofs); + BOOST_CHECK_EQUAL(cpw.size(), N); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + BOOST_CHECK_NO_THROW(ss << cpw); + + CompactProofs cpr; + BOOST_CHECK_NO_THROW(ss >> cpr); + + BOOST_CHECK_EQUAL(cpr.size(), N); + BOOST_CHECK_EQUAL(cpr.getKeys().first, cpw.getKeys().first); + BOOST_CHECK_EQUAL(cpr.getKeys().second, cpw.getKeys().second); + + auto shortIds = TestCompactProofs::getShortProofIds(cpr); + size_t index = 0; + proofs.forEachLeaf([&](auto pLeaf) { + const ProofId &proofid = pLeaf->proof->getId(); + BOOST_CHECK_EQUAL(cpr.getShortID(proofid), cpw.getShortID(proofid)); + BOOST_CHECK_EQUAL(cpr.getShortID(proofid), shortIds[index]); + ++index; + }); + } +} + +BOOST_AUTO_TEST_SUITE_END()