diff --git a/src/avalanche.h b/src/avalanche.h --- a/src/avalanche.h +++ b/src/avalanche.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_AVALANCHE_H #define BITCOIN_AVALANCHE_H -#include "net.h" // for NodeId +#include "net.h" #include "protocol.h" // for CInv #include "rwcollection.h" #include "serialize.h" @@ -130,6 +130,23 @@ } }; +class AvalanchePoll { + std::vector invs; + +public: + AvalanchePoll(std::vector invsIn) : invs(invsIn) {} + + const std::vector &GetInvs() const { return invs; } + + // serialization support + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream &s, Operation ser_action) { + READWRITE(invs); + } +}; + class AvalancheProcessor { private: /** @@ -158,6 +175,12 @@ bool startEventLoop(CScheduler &scheduler); bool stopEventLoop(); + +private: + void runEventLoop(); + std::vector getInvsForNextPoll() const; + + friend struct AvalancheTest; }; #endif // BITCOIN_AVALANCHE_H diff --git a/src/avalanche.cpp b/src/avalanche.cpp --- a/src/avalanche.cpp +++ b/src/avalanche.cpp @@ -59,6 +59,10 @@ * Run the avalanche event loop every 20ms. */ static int64_t AVALANCHE_TIME_STEP_MILISECONDS = 10; +/** + * Maximum item that can be polled at once. + */ +static size_t AVALANCHE_MAX_ELEMENT_POLL = 4096; } bool AvalancheProcessor::startEventLoop(CScheduler &scheduler) { @@ -105,3 +109,30 @@ stopRequest = false; return true; } + +std::vector AvalancheProcessor::getInvsForNextPoll() const { + std::vector invs; + + auto r = vote_records.getReadView(); + for (const std::pair &p : r) { + const VoteRecord &v = p.second; + if (v.hasFinalized()) { + // If this has finalized, we can just skip. + continue; + } + + // We don't have a decision, we need more votes. + invs.push_back(CInv(MSG_BLOCK, p.first)); + if (invs.size() >= AVALANCHE_MAX_ELEMENT_POLL) { + // Make sure we do not produce more invs than specified by the + // protocol. + return invs; + } + } + + return invs; +} + +void AvalancheProcessor::runEventLoop() { + std::vector invs = getInvsForNextPoll(); +} diff --git a/src/test/avalanche_tests.cpp b/src/test/avalanche_tests.cpp --- a/src/test/avalanche_tests.cpp +++ b/src/test/avalanche_tests.cpp @@ -9,6 +9,12 @@ #include +struct AvalancheTest { + static std::vector getInvsForNextPoll(const AvalancheProcessor &p) { + return p.getInvsForNextPoll(); + } +}; + BOOST_FIXTURE_TEST_SUITE(avalanche_tests, BasicTestingSetup) #define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ @@ -72,20 +78,27 @@ CBlockIndex index; // Make sure the block has a hash. - const uint256 zeroHash; - index.phashBlock = &zeroHash; + static const uint256 blockHash(uint256S( + "abcdef0000000000000000000000000000000000000000000000000000000001")); + index.phashBlock = &blockHash; // Querying for random block returns false. BOOST_CHECK(!p.isAccepted(&index)); BOOST_CHECK(!p.hasFinalized(&index)); - // Newly added blocks are also considered rejected. + // Add a new block. Check it is added to the polls. BOOST_CHECK(p.addBlockToReconcile(&index)); + auto invs = AvalancheTest::getInvsForNextPoll(p); + BOOST_CHECK_EQUAL(invs.size(), 1); + BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); + BOOST_CHECK(invs[0].hash == blockHash); + + // Newly added blocks are also considered rejected. BOOST_CHECK(!p.isAccepted(&index)); BOOST_CHECK(!p.hasFinalized(&index)); // Let's vote for this block a few times. - AvalancheResponse resp{{AvalancheVote(zeroHash, 0)}}; + AvalancheResponse resp{{AvalancheVote(blockHash, 0)}}; for (int i = 0; i < 5; i++) { p.registerVotes(resp); BOOST_CHECK(!p.isAccepted(&index)); @@ -99,12 +112,22 @@ BOOST_CHECK(!p.hasFinalized(&index)); } + // As long as it is not finalized, we poll. + invs = AvalancheTest::getInvsForNextPoll(p); + BOOST_CHECK_EQUAL(invs.size(), 1); + BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); + BOOST_CHECK(invs[0].hash == blockHash); + // Now finalize the decision. - resp = {{AvalancheVote(zeroHash, 1)}}; + resp = {{AvalancheVote(blockHash, 1)}}; p.registerVotes(resp); BOOST_CHECK(p.isAccepted(&index)); BOOST_CHECK(p.hasFinalized(&index)); + // Once the decision is finalized, there is no poll for it. + invs = AvalancheTest::getInvsForNextPoll(p); + BOOST_CHECK_EQUAL(invs.size(), 0); + // Now let's undo this and finalize rejection. for (int i = 0; i < 5; i++) { p.registerVotes(resp); @@ -119,11 +142,21 @@ BOOST_CHECK(!p.hasFinalized(&index)); } + // As long as it is not finalized, we poll. + invs = AvalancheTest::getInvsForNextPoll(p); + BOOST_CHECK_EQUAL(invs.size(), 1); + BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); + BOOST_CHECK(invs[0].hash == blockHash); + // Now finalize the decision. p.registerVotes(resp); BOOST_CHECK(!p.isAccepted(&index)); BOOST_CHECK(p.hasFinalized(&index)); + // Once the decision is finalized, there is no poll for it. + invs = AvalancheTest::getInvsForNextPoll(p); + BOOST_CHECK_EQUAL(invs.size(), 0); + // Adding the block twice does nothing. BOOST_CHECK(!p.addBlockToReconcile(&index)); BOOST_CHECK(!p.isAccepted(&index));