diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,7 @@ BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ + avalanche_impl.h \ base58.h \ bloom.h \ blockencodings.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -25,11 +25,12 @@ # test_bitcoin binary # BITCOIN_TESTS =\ - test/arith_uint256_tests.cpp \ test/scriptnum10.h \ test/addrman_tests.cpp \ - test/amount_tests.cpp \ test/allocator_tests.cpp \ + test/amount_tests.cpp \ + test/arith_uint256_tests.cpp \ + test/avalanche_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ diff --git a/src/avalanche_impl.h b/src/avalanche_impl.h new file mode 100644 --- /dev/null +++ b/src/avalanche_impl.h @@ -0,0 +1,82 @@ +// Copyright (c) 2018 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_IMPL_H +#define BITCOIN_AVALANCHE_IMPL_H + +#include + +namespace { +/** + * Finalization score. + */ +static int AVALANCHE_FINALIZATION_SCORE = 128; + +/** + * Vote history. + */ +struct VoteRecord { +private: + // Historical record of votes. + uint16_t votes; + + // confidence's LSB bit is the result. Higher bits are actual confidence + // score. + uint16_t confidence; + + /** + * Return the number of bits set in an integer value. + * TODO: There are compiler intrinsics to do that, but we'd need to get them + * detected so this will do for now. + */ + static uint32_t countBits(uint32_t value) { + uint32_t count = 0; + while (value) { + // If the value is non zero, then at least one bit is set. + count++; + + // Clear the rightmost bit set. + value &= (value - 1); + } + + return count; + } + +public: + VoteRecord() : votes(0xaaaa), confidence(0) {} + + bool isValid() const { return confidence & 0x01; } + + uint16_t getConfidence() const { return confidence >> 1; } + bool hasFinalized() const { + return getConfidence() >= AVALANCHE_FINALIZATION_SCORE; + } + + bool registerVote(bool vote) { + votes = (votes << 1) | vote; + + auto bits = countBits(votes & 0xff); + bool yes = bits > 6; + bool no = bits < 2; + if (!yes && !no) { + // The vote is inconclusive. + return false; + } + + if (isValid() == yes) { + // If the vote is in agreement with our internal status, increase + // confidence. + confidence += 2; + } else { + // The vote did not agree with our internal state, in that case, + // reset confidence. + confidence = yes; + } + + return true; + } +}; +} + +#endif // BITCOIN_AVALANCHE_IMPL_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -44,10 +44,11 @@ add_dependencies(check check-bitcoin) add_test_to_suite(bitcoin test_bitcoin - arith_uint256_tests.cpp addrman_tests.cpp - amount_tests.cpp allocator_tests.cpp + amount_tests.cpp + arith_uint256_tests.cpp + avalanche_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp diff --git a/src/test/avalanche_tests.cpp b/src/test/avalanche_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/avalanche_tests.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2010 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "avalanche_impl.h" + +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(avalanche_tests, BasicTestingSetup) + +#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ + vr.registerVote(vote); \ + BOOST_CHECK_EQUAL(vr.isValid(), state); \ + BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \ + BOOST_CHECK_EQUAL(vr.getConfidence(), confidence); + +BOOST_AUTO_TEST_CASE(vote_record) { + VoteRecord vr; + + // Check initial state. + BOOST_CHECK_EQUAL(vr.isValid(), false); + BOOST_CHECK_EQUAL(vr.hasFinalized(), false); + BOOST_CHECK_EQUAL(vr.getConfidence(), 0); + + // We register one vote for, which keep things at 4/4. + REGISTER_VOTE_AND_CHECK(vr, true, false, false, 0); + + // One more and we are at 5/3. + REGISTER_VOTE_AND_CHECK(vr, true, false, false, 0); + + // One more and we are at 5/3. + REGISTER_VOTE_AND_CHECK(vr, true, false, false, 0); + + // One more and we are at 6/2. + REGISTER_VOTE_AND_CHECK(vr, true, false, false, 0); + + // One more and we are at 6/2. + REGISTER_VOTE_AND_CHECK(vr, true, false, false, 0); + + // Next vote will flip state, and confidence will increase as long as we + // vote yes. + for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) { + REGISTER_VOTE_AND_CHECK(vr, true, true, false, i); + } + + // The next vote will finalize the decision. + REGISTER_VOTE_AND_CHECK(vr, false, true, true, + AVALANCHE_FINALIZATION_SCORE); + + // Now that we have two no votes, confidence stop increasing. + for (int i = 0; i < 5; i++) { + REGISTER_VOTE_AND_CHECK(vr, false, true, true, + AVALANCHE_FINALIZATION_SCORE); + } + + // Next vote will flip state, and confidence will increase as long as we + // vote no. + for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) { + REGISTER_VOTE_AND_CHECK(vr, false, false, false, i); + } + + // The next vote will finalize the decision. + REGISTER_VOTE_AND_CHECK(vr, true, false, true, + AVALANCHE_FINALIZATION_SCORE); +} + +BOOST_AUTO_TEST_SUITE_END()