diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -36,6 +36,7 @@ test/bip32_tests.cpp \ test/blockcheck_tests.cpp \ test/blockencodings_tests.cpp \ + test/blockstatus_tests.cpp \ test/bloom_tests.cpp \ test/bswap_tests.cpp \ test/cashaddr_tests.cpp \ diff --git a/src/chain.h b/src/chain.h --- a/src/chain.h +++ b/src/chain.h @@ -188,6 +188,9 @@ BLOCK_VALID_TRANSACTIONS | BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, + // Just a helper + BLOCK_HAVE_NOTHING = 0, + // Full block available in blk*.dat BLOCK_HAVE_DATA = 8, // Undo data available in rev*.dat @@ -201,6 +204,72 @@ BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, }; +struct BlockStatus { +private: + uint32_t status; + + explicit BlockStatus(uint32_t nStatusIn) : status(nStatusIn) {} + +public: + explicit BlockStatus() : status(BLOCK_VALID_UNKNOWN) {} + + BlockValidity getValidity() const { + return BlockValidity(status & BLOCK_VALID_MASK); + } + + BlockStatus withValidity(BlockValidity validity) const { + return BlockStatus((status & ~BLOCK_VALID_MASK) | uint32_t(validity)); + } + + bool hasData() const { return status & BLOCK_HAVE_DATA; } + BlockStatus withData(bool hasData = true) const { + return BlockStatus((status & ~BLOCK_HAVE_DATA) | + (hasData ? BLOCK_HAVE_DATA : BLOCK_HAVE_NOTHING)); + } + + bool hasUndo() const { return status & BLOCK_HAVE_UNDO; } + BlockStatus withUndo(bool hasUndo = true) const { + return BlockStatus((status & ~BLOCK_HAVE_UNDO) | + (hasUndo ? BLOCK_HAVE_UNDO : BLOCK_HAVE_NOTHING)); + } + + /** + * Check whether this block index entry is valid up to the passed validity + * level. + */ + bool isValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { + if (status & BLOCK_FAILED_MASK) { + return false; + } + + return getValidity() >= nUpTo; + } + + // To transition from this and the plain old intereger. + // TODO: delete. + uint32_t operator&(uint32_t rhs) const { return status & rhs; } + uint32_t operator|(uint32_t rhs) const { return status | rhs; } + + BlockStatus &operator&=(uint32_t rhs) { + this->status &= rhs; + return *this; + } + BlockStatus &operator|=(uint32_t rhs) { + this->status |= rhs; + return *this; + } + + operator bool() const { return status != 0; } + operator uint32_t() const { return status; } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream &s, Operation ser_action) { + READWRITE(VARINT(status)); + } +}; + /** * The block chain is a tree shaped structure starting with the genesis block at * the root, with each block potentially having multiple candidates to be the @@ -248,7 +317,7 @@ unsigned int nChainTx; //! Verification status of this block. See enum BlockStatus - uint32_t nStatus; + BlockStatus nStatus; //! block header int32_t nVersion; @@ -278,7 +347,7 @@ nChainWork = arith_uint256(); nTx = 0; nChainTx = 0; - nStatus = 0; + nStatus = BlockStatus(); nSequenceId = 0; nTimeMax = 0; @@ -372,12 +441,7 @@ //! Check whether this block index entry is valid up to the passed validity //! level. bool IsValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { - // Only validity flags allowed. - if (nStatus & BLOCK_FAILED_MASK) { - return false; - } - - return BlockValidity(nStatus & BLOCK_VALID_MASK) >= nUpTo; + return nStatus.isValid(nUpTo); } //! Raise the validity level of this block index entry. @@ -392,7 +456,7 @@ return false; } - nStatus = (nStatus & ~BLOCK_VALID_MASK) | uint32_t(nUpTo); + nStatus = nStatus.withValidity(nUpTo); return true; } @@ -452,7 +516,7 @@ } READWRITE(VARINT(nHeight)); - READWRITE(VARINT(nStatus)); + READWRITE(nStatus); READWRITE(VARINT(nTx)); if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) { READWRITE(VARINT(nFile)); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -52,6 +52,7 @@ bip32_tests.cpp blockcheck_tests.cpp blockencodings_tests.cpp + blockstatus_tests.cpp bloom_tests.cpp bswap_tests.cpp cashaddr_tests.cpp diff --git a/src/test/blockstatus_tests.cpp b/src/test/blockstatus_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/blockstatus_tests.cpp @@ -0,0 +1,62 @@ +// 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. + +#include "chain.h" +#include "test/test_bitcoin.h" + +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(blockstatus_tests, BasicTestingSetup) + +static void CheckBlockStatus(BlockStatus s, BlockValidity validity, + bool hasData, bool hasUndo) { + BOOST_CHECK(s.getValidity() == validity); + BOOST_CHECK_EQUAL(s.hasData(), hasData); + BOOST_CHECK_EQUAL(s.hasUndo(), hasUndo); +} + +BOOST_AUTO_TEST_CASE(sighash_construction_test) { + // Check default values. + CheckBlockStatus(BlockStatus(), BlockValidity::UNKNOWN, false, false); + + // Check all possible permutations. + std::set baseValidities{ + BlockValidity::UNKNOWN, BlockValidity::HEADER, + BlockValidity::TREE, BlockValidity::TRANSACTIONS, + BlockValidity::CHAIN, BlockValidity::SCRIPTS}; + std::set hasDataValues{false, true}; + std::set hasUndoValues{false, true}; + + for (BlockValidity validity : baseValidities) { + for (bool hasData : hasDataValues) { + for (bool hasUndo : hasUndoValues) { + const BlockStatus s = BlockStatus() + .withValidity(validity) + .withData(hasData) + .withUndo(hasUndo); + + CheckBlockStatus(s, validity, hasData, hasUndo); + + // Also check all possible alterations. + CheckBlockStatus(s.withData(hasData), validity, hasData, + hasUndo); + CheckBlockStatus(s.withData(!hasData), validity, !hasData, + hasUndo); + CheckBlockStatus(s.withUndo(hasUndo), validity, hasData, + hasUndo); + CheckBlockStatus(s.withUndo(!hasUndo), validity, hasData, + !hasUndo); + + for (BlockValidity newValidity : baseValidities) { + CheckBlockStatus(s.withValidity(newValidity), newValidity, + hasData, hasUndo); + } + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END()