aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/blockfilter_index_tests.cpp8
-rw-r--r--src/test/coinstatsindex_tests.cpp2
-rw-r--r--src/test/fuzz/bitdeque.cpp542
-rw-r--r--src/test/fuzz/pow.cpp37
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp2
-rw-r--r--src/test/headers_sync_chainwork_tests.cpp146
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/pow_tests.cpp27
-rw-r--r--src/test/skiplist_tests.cpp2
-rw-r--r--src/test/util/mining.cpp2
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util_tests.cpp1
-rw-r--r--src/test/validation_block_tests.cpp10
-rw-r--r--src/test/validation_chainstate_tests.cpp2
14 files changed, 765 insertions, 20 deletions
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 1a182209b8..2798e998af 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -101,7 +101,7 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex,
CBlockHeader header = block->GetBlockHeader();
BlockValidationState state;
- if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, &pindex)) {
+ if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, true, state, &pindex)) {
return false;
}
}
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
@@ -227,7 +227,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// Reorg back to chain A.
for (size_t i = 2; i < 4; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index c93d05a93b..132c4e53e7 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -102,7 +102,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
LOCK(cs_main);
BlockValidationState state;
BOOST_CHECK(CheckBlock(block, state, params.GetConsensus()));
- BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr));
+ BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true));
CCoinsViewCache view(&chainstate.CoinsTip());
BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view));
}
diff --git a/src/test/fuzz/bitdeque.cpp b/src/test/fuzz/bitdeque.cpp
new file mode 100644
index 0000000000..01af8320b5
--- /dev/null
+++ b/src/test/fuzz/bitdeque.cpp
@@ -0,0 +1,542 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <util/bitdeque.h>
+
+#include <random.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/util.h>
+
+#include <deque>
+#include <vector>
+
+namespace {
+
+constexpr int LEN_BITS = 16;
+constexpr int RANDDATA_BITS = 20;
+
+using bitdeque_type = bitdeque<128>;
+
+//! Deterministic random vector of bools, for begin/end insertions to draw from.
+std::vector<bool> RANDDATA;
+
+void InitRandData()
+{
+ FastRandomContext ctx(true);
+ RANDDATA.clear();
+ for (size_t i = 0; i < (1U << RANDDATA_BITS) + (1U << LEN_BITS); ++i) {
+ RANDDATA.push_back(ctx.randbool());
+ }
+}
+
+} // namespace
+
+FUZZ_TARGET_INIT(bitdeque, InitRandData)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ FastRandomContext ctx(true);
+
+ size_t maxlen = (1U << provider.ConsumeIntegralInRange<size_t>(0, LEN_BITS)) - 1;
+ size_t limitlen = 4 * maxlen;
+
+ std::deque<bool> deq;
+ bitdeque_type bitdeq;
+
+ const auto& cdeq = deq;
+ const auto& cbitdeq = bitdeq;
+
+ size_t initlen = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ while (initlen) {
+ bool val = ctx.randbool();
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ --initlen;
+ }
+
+ while (provider.remaining_bytes()) {
+ {
+ assert(deq.size() == bitdeq.size());
+ auto it = deq.begin();
+ auto bitit = bitdeq.begin();
+ auto itend = deq.end();
+ while (it != itend) {
+ assert(*it == *bitit);
+ ++it;
+ ++bitit;
+ }
+ }
+
+ CallOneOf(provider,
+ [&] {
+ // constructor()
+ deq = std::deque<bool>{};
+ bitdeq = bitdeque_type{};
+ },
+ [&] {
+ // clear()
+ deq.clear();
+ bitdeq.clear();
+ },
+ [&] {
+ // resize()
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ deq.resize(count);
+ bitdeq.resize(count);
+ },
+ [&] {
+ // assign(count, val)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ deq.assign(count, val);
+ bitdeq.assign(count, val);
+ },
+ [&] {
+ // constructor(count, val)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ deq = std::deque<bool>(count, val);
+ bitdeq = bitdeque_type(count, val);
+ },
+ [&] {
+ // constructor(count)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ deq = std::deque<bool>(count);
+ bitdeq = bitdeque_type(count);
+ },
+ [&] {
+ // construct(begin, end)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ deq = std::deque<bool>(rand_begin, rand_end);
+ bitdeq = bitdeque_type(rand_begin, rand_end);
+ },
+ [&] {
+ // assign(begin, end)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ deq.assign(rand_begin, rand_end);
+ bitdeq.assign(rand_begin, rand_end);
+ },
+ [&] {
+ // construct(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq = std::deque<bool>(ilist);
+ bitdeq = bitdeque_type(ilist);
+ },
+ [&] {
+ // assign(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq.assign(ilist);
+ bitdeq.assign(ilist);
+ },
+ [&] {
+ // operator=(const&)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ const std::deque<bool> deq2(count, val);
+ deq = deq2;
+ const bitdeque_type bitdeq2(count, val);
+ bitdeq = bitdeq2;
+ },
+ [&] {
+ // operator=(&&)
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ bool val = ctx.randbool();
+ std::deque<bool> deq2(count, val);
+ deq = std::move(deq2);
+ bitdeque_type bitdeq2(count, val);
+ bitdeq = std::move(bitdeq2);
+ },
+ [&] {
+ // deque swap
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ std::deque<bool> deq2(rand_begin, rand_end);
+ bitdeque_type bitdeq2(rand_begin, rand_end);
+ using std::swap;
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ swap(deq, deq2);
+ swap(bitdeq, bitdeq2);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ },
+ [&] {
+ // deque.swap
+ auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ std::deque<bool> deq2(rand_begin, rand_end);
+ bitdeque_type bitdeq2(rand_begin, rand_end);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ deq.swap(deq2);
+ bitdeq.swap(bitdeq2);
+ assert(deq.size() == bitdeq.size());
+ assert(deq2.size() == bitdeq2.size());
+ },
+ [&] {
+ // operator=(initializer_list)
+ std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
+ deq = ilist;
+ bitdeq = ilist;
+ },
+ [&] {
+ // iterator arithmetic
+ auto pos1 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
+ auto pos2 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
+ auto it = deq.begin() + pos1;
+ auto bitit = bitdeq.begin() + pos1;
+ if ((size_t)pos1 != cdeq.size()) assert(*it == *bitit);
+ assert(it - deq.begin() == pos1);
+ assert(bitit - bitdeq.begin() == pos1);
+ if (provider.ConsumeBool()) {
+ it += pos2 - pos1;
+ bitit += pos2 - pos1;
+ } else {
+ it -= pos1 - pos2;
+ bitit -= pos1 - pos2;
+ }
+ if ((size_t)pos2 != cdeq.size()) assert(*it == *bitit);
+ assert(deq.end() - it == bitdeq.end() - bitit);
+ if (provider.ConsumeBool()) {
+ if ((size_t)pos2 != cdeq.size()) {
+ ++it;
+ ++bitit;
+ }
+ } else {
+ if (pos2 != 0) {
+ --it;
+ --bitit;
+ }
+ }
+ assert(deq.end() - it == bitdeq.end() - bitit);
+ },
+ [&] {
+ // begin() and end()
+ assert(deq.end() - deq.begin() == bitdeq.end() - bitdeq.begin());
+ },
+ [&] {
+ // begin() and end() (const)
+ assert(cdeq.end() - cdeq.begin() == cbitdeq.end() - cbitdeq.begin());
+ },
+ [&] {
+ // rbegin() and rend()
+ assert(deq.rend() - deq.rbegin() == bitdeq.rend() - bitdeq.rbegin());
+ },
+ [&] {
+ // rbegin() and rend() (const)
+ assert(cdeq.rend() - cdeq.rbegin() == cbitdeq.rend() - cbitdeq.rbegin());
+ },
+ [&] {
+ // cbegin() and cend()
+ assert(cdeq.cend() - cdeq.cbegin() == cbitdeq.cend() - cbitdeq.cbegin());
+ },
+ [&] {
+ // crbegin() and crend()
+ assert(cdeq.crend() - cdeq.crbegin() == cbitdeq.crend() - cbitdeq.crbegin());
+ },
+ [&] {
+ // size() and maxsize()
+ assert(cdeq.size() == cbitdeq.size());
+ assert(cbitdeq.size() <= cbitdeq.max_size());
+ },
+ [&] {
+ // empty
+ assert(cdeq.empty() == cbitdeq.empty());
+ },
+ [&] {
+ // at (in range) and flip
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq.at(pos);
+ auto bitref = bitdeq.at(pos);
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref.flip();
+ }
+ }
+ },
+ [&] {
+ // at (maybe out of range) and bit assign
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
+ bool newval = ctx.randbool();
+ bool throw_deq{false}, throw_bitdeq{false};
+ bool val_deq{false}, val_bitdeq{false};
+ try {
+ auto& ref = deq.at(pos);
+ val_deq = ref;
+ ref = newval;
+ } catch (const std::out_of_range&) {
+ throw_deq = true;
+ }
+ try {
+ auto ref = bitdeq.at(pos);
+ val_bitdeq = ref;
+ ref = newval;
+ } catch (const std::out_of_range&) {
+ throw_bitdeq = true;
+ }
+ assert(throw_deq == throw_bitdeq);
+ assert(throw_bitdeq == (pos >= cdeq.size()));
+ if (!throw_deq) assert(val_deq == val_bitdeq);
+ },
+ [&] {
+ // at (maybe out of range) (const)
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
+ bool throw_deq{false}, throw_bitdeq{false};
+ bool val_deq{false}, val_bitdeq{false};
+ try {
+ auto& ref = cdeq.at(pos);
+ val_deq = ref;
+ } catch (const std::out_of_range&) {
+ throw_deq = true;
+ }
+ try {
+ auto ref = cbitdeq.at(pos);
+ val_bitdeq = ref;
+ } catch (const std::out_of_range&) {
+ throw_bitdeq = true;
+ }
+ assert(throw_deq == throw_bitdeq);
+ assert(throw_bitdeq == (pos >= cdeq.size()));
+ if (!throw_deq) assert(val_deq == val_bitdeq);
+ },
+ [&] {
+ // operator[]
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ assert(deq[pos] == bitdeq[pos]);
+ if (ctx.randbool()) {
+ deq[pos] = !deq[pos];
+ bitdeq[pos].flip();
+ }
+ }
+ },
+ [&] {
+ // operator[] const
+ if (!cdeq.empty()) {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ assert(deq[pos] == bitdeq[pos]);
+ }
+ },
+ [&] {
+ // front()
+ if (!cdeq.empty()) {
+ auto& ref = deq.front();
+ auto bitref = bitdeq.front();
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref = !bitref;
+ }
+ }
+ },
+ [&] {
+ // front() const
+ if (!cdeq.empty()) {
+ auto& ref = cdeq.front();
+ auto bitref = cbitdeq.front();
+ assert(ref == bitref);
+ }
+ },
+ [&] {
+ // back() and swap(bool, ref)
+ if (!cdeq.empty()) {
+ auto& ref = deq.back();
+ auto bitref = bitdeq.back();
+ assert(ref == bitref);
+ if (ctx.randbool()) {
+ ref = !ref;
+ bitref.flip();
+ }
+ }
+ },
+ [&] {
+ // back() const
+ if (!cdeq.empty()) {
+ const auto& cdeq = deq;
+ const auto& cbitdeq = bitdeq;
+ auto& ref = cdeq.back();
+ auto bitref = cbitdeq.back();
+ assert(ref == bitref);
+ }
+ },
+ [&] {
+ // push_back()
+ if (cdeq.size() < limitlen) {
+ bool val = ctx.randbool();
+ if (cdeq.empty()) {
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.push_back(val);
+ bitdeq.push_back(val);
+ assert(ref == bitref); // references are not invalidated
+ }
+ }
+ },
+ [&] {
+ // push_front()
+ if (cdeq.size() < limitlen) {
+ bool val = ctx.randbool();
+ if (cdeq.empty()) {
+ deq.push_front(val);
+ bitdeq.push_front(val);
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.push_front(val);
+ bitdeq.push_front(val);
+ assert(ref == bitref); // references are not invalidated
+ }
+ }
+ },
+ [&] {
+ // pop_back()
+ if (!cdeq.empty()) {
+ if (cdeq.size() == 1) {
+ deq.pop_back();
+ bitdeq.pop_back();
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 2);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.pop_back();
+ bitdeq.pop_back();
+ assert(ref == bitref); // references to other elements are not invalidated
+ }
+ }
+ },
+ [&] {
+ // pop_front()
+ if (!cdeq.empty()) {
+ if (cdeq.size() == 1) {
+ deq.pop_front();
+ bitdeq.pop_front();
+ } else {
+ size_t pos = provider.ConsumeIntegralInRange<size_t>(1, cdeq.size() - 1);
+ auto& ref = deq[pos];
+ auto bitref = bitdeq[pos];
+ assert(ref == bitref);
+ deq.pop_front();
+ bitdeq.pop_front();
+ assert(ref == bitref); // references to other elements are not invalidated
+ }
+ }
+ },
+ [&] {
+ // erase (in middle, single)
+ if (!cdeq.empty()) {
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
+ size_t after = cdeq.size() - 1 - before;
+ auto it = deq.erase(cdeq.begin() + before);
+ auto bitit = bitdeq.erase(cbitdeq.begin() + before);
+ assert(it == cdeq.begin() + before && it == cdeq.end() - after);
+ assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
+ }
+ },
+ [&] {
+ // erase (at front, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto it = deq.erase(cdeq.begin(), cdeq.begin() + count);
+ auto bitit = bitdeq.erase(cbitdeq.begin(), cbitdeq.begin() + count);
+ assert(it == deq.begin());
+ assert(bitit == bitdeq.begin());
+ },
+ [&] {
+ // erase (at back, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto it = deq.erase(cdeq.end() - count, cdeq.end());
+ auto bitit = bitdeq.erase(cbitdeq.end() - count, cbitdeq.end());
+ assert(it == deq.end());
+ assert(bitit == bitdeq.end());
+ },
+ [&] {
+ // erase (in middle, range)
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - count);
+ size_t after = cdeq.size() - count - before;
+ auto it = deq.erase(cdeq.begin() + before, cdeq.end() - after);
+ auto bitit = bitdeq.erase(cbitdeq.begin() + before, cbitdeq.end() - after);
+ assert(it == cdeq.begin() + before && it == cdeq.end() - after);
+ assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
+ },
+ [&] {
+ // insert/emplace (in middle, single)
+ if (cdeq.size() < limitlen) {
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ bool val = ctx.randbool();
+ bool do_emplace = provider.ConsumeBool();
+ auto it = deq.insert(cdeq.begin() + before, val);
+ auto bitit = do_emplace ? bitdeq.emplace(cbitdeq.begin() + before, val)
+ : bitdeq.insert(cbitdeq.begin() + before, val);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ },
+ [&] {
+ // insert (at front, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.begin(), rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.begin(), rand_begin, rand_end);
+ assert(it == cdeq.begin());
+ assert(bitit == cbitdeq.begin());
+ }
+ },
+ [&] {
+ // insert (at back, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.end(), rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.end(), rand_begin, rand_end);
+ assert(it == cdeq.end() - count);
+ assert(bitit == cbitdeq.end() - count);
+ }
+ },
+ [&] {
+ // insert (in middle, range)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ bool val = ctx.randbool();
+ auto it = deq.insert(cdeq.begin() + before, count, val);
+ auto bitit = bitdeq.insert(cbitdeq.begin() + before, count, val);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ },
+ [&] {
+ // insert (in middle, begin/end)
+ if (cdeq.size() < limitlen) {
+ size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
+ size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
+ auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
+ auto rand_end = rand_begin + count;
+ auto it = deq.insert(cdeq.begin() + before, rand_begin, rand_end);
+ auto bitit = bitdeq.insert(cbitdeq.begin() + before, rand_begin, rand_end);
+ assert(it == deq.begin() + before);
+ assert(bitit == bitdeq.begin() + before);
+ }
+ }
+ );
+ }
+
+}
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index 0004d82d66..507ce57ec0 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -83,3 +83,40 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
}
}
}
+
+
+FUZZ_TARGET_INIT(pow_transition, initialize_pow)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const Consensus::Params& consensus_params{Params().GetConsensus()};
+ std::vector<std::unique_ptr<CBlockIndex>> blocks;
+
+ const uint32_t old_time{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ const uint32_t new_time{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ const int32_t version{fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ uint32_t nbits{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+
+ const arith_uint256 pow_limit = UintToArith256(consensus_params.powLimit);
+ arith_uint256 old_target;
+ old_target.SetCompact(nbits);
+ if (old_target > pow_limit) {
+ nbits = pow_limit.GetCompact();
+ }
+ // Create one difficulty adjustment period worth of headers
+ for (int height = 0; height < consensus_params.DifficultyAdjustmentInterval(); ++height) {
+ CBlockHeader header;
+ header.nVersion = version;
+ header.nTime = old_time;
+ header.nBits = nbits;
+ if (height == consensus_params.DifficultyAdjustmentInterval() - 1) {
+ header.nTime = new_time;
+ }
+ auto current_block{std::make_unique<CBlockIndex>(header)};
+ current_block->pprev = blocks.empty() ? nullptr : blocks.back().get();
+ current_block->nHeight = height;
+ blocks.emplace_back(std::move(current_block)).get();
+ }
+ auto last_block{blocks.back().get()};
+ unsigned int new_nbits{GetNextWorkRequired(last_block, nullptr, consensus_params)};
+ Assert(PermittedDifficultyTransition(consensus_params, last_block->nHeight + 1, last_block->nBits, new_nbits));
+}
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index 0b596492be..8abb943266 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -58,7 +58,7 @@ FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
if (fuzzed_data_provider.ConsumeBool()) {
for (const auto& block : *g_chain) {
BlockValidationState dummy;
- bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy)};
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)};
Assert(processed);
const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
Assert(index);
diff --git a/src/test/headers_sync_chainwork_tests.cpp b/src/test/headers_sync_chainwork_tests.cpp
new file mode 100644
index 0000000000..41241ebee2
--- /dev/null
+++ b/src/test/headers_sync_chainwork_tests.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2022 The Bitcoin Core 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 <chainparams.h>
+#include <consensus/params.h>
+#include <headerssync.h>
+#include <pow.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+
+struct HeadersGeneratorSetup : public RegTestingSetup {
+ /** Search for a nonce to meet (regtest) proof of work */
+ void FindProofOfWork(CBlockHeader& starting_header);
+ /**
+ * Generate headers in a chain that build off a given starting hash, using
+ * the given nVersion, advancing time by 1 second from the starting
+ * prev_time, and with a fixed merkle root hash.
+ */
+ void GenerateHeaders(std::vector<CBlockHeader>& headers, size_t count,
+ const uint256& starting_hash, const int nVersion, int prev_time,
+ const uint256& merkle_root, const uint32_t nBits);
+};
+
+void HeadersGeneratorSetup::FindProofOfWork(CBlockHeader& starting_header)
+{
+ while (!CheckProofOfWork(starting_header.GetHash(), starting_header.nBits, Params().GetConsensus())) {
+ ++(starting_header.nNonce);
+ }
+}
+
+void HeadersGeneratorSetup::GenerateHeaders(std::vector<CBlockHeader>& headers,
+ size_t count, const uint256& starting_hash, const int nVersion, int prev_time,
+ const uint256& merkle_root, const uint32_t nBits)
+{
+ uint256 prev_hash = starting_hash;
+
+ while (headers.size() < count) {
+ headers.push_back(CBlockHeader());
+ CBlockHeader& next_header = headers.back();;
+ next_header.nVersion = nVersion;
+ next_header.hashPrevBlock = prev_hash;
+ next_header.hashMerkleRoot = merkle_root;
+ next_header.nTime = prev_time+1;
+ next_header.nBits = nBits;
+
+ FindProofOfWork(next_header);
+ prev_hash = next_header.GetHash();
+ prev_time = next_header.nTime;
+ }
+ return;
+}
+
+BOOST_FIXTURE_TEST_SUITE(headers_sync_chainwork_tests, HeadersGeneratorSetup)
+
+// In this test, we construct two sets of headers from genesis, one with
+// sufficient proof of work and one without.
+// 1. We deliver the first set of headers and verify that the headers sync state
+// updates to the REDOWNLOAD phase successfully.
+// 2. Then we deliver the second set of headers and verify that they fail
+// processing (presumably due to commitments not matching).
+// 3. Finally, we verify that repeating with the first set of headers in both
+// phases is successful.
+BOOST_AUTO_TEST_CASE(headers_sync_state)
+{
+ std::vector<CBlockHeader> first_chain;
+ std::vector<CBlockHeader> second_chain;
+
+ std::unique_ptr<HeadersSyncState> hss;
+
+ const int target_blocks = 15000;
+ arith_uint256 chain_work = target_blocks*2;
+
+ // Generate headers for two different chains (using differing merkle roots
+ // to ensure the headers are different).
+ GenerateHeaders(first_chain, target_blocks-1, Params().GenesisBlock().GetHash(),
+ Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
+ ArithToUint256(0), Params().GenesisBlock().nBits);
+
+ GenerateHeaders(second_chain, target_blocks-2, Params().GenesisBlock().GetHash(),
+ Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
+ ArithToUint256(1), Params().GenesisBlock().nBits);
+
+ const CBlockIndex* chain_start = WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(Params().GenesisBlock().GetHash()));
+ std::vector<CBlockHeader> headers_batch;
+
+ // Feed the first chain to HeadersSyncState, by delivering 1 header
+ // initially and then the rest.
+ headers_batch.insert(headers_batch.end(), std::next(first_chain.begin()), first_chain.end());
+
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ (void)hss->ProcessNextHeaders({first_chain.front()}, true);
+ // Pretend the first header is still "full", so we don't abort.
+ auto result = hss->ProcessNextHeaders(headers_batch, true);
+
+ // This chain should look valid, and we should have met the proof-of-work
+ // requirement.
+ BOOST_CHECK(result.success);
+ BOOST_CHECK(result.request_more);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
+
+ // Try to sneakily feed back the second chain.
+ result = hss->ProcessNextHeaders(second_chain, true);
+ BOOST_CHECK(!result.success); // foiled!
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+
+ // Now try again, this time feeding the first chain twice.
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ (void)hss->ProcessNextHeaders(first_chain, true);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
+
+ result = hss->ProcessNextHeaders(first_chain, true);
+ BOOST_CHECK(result.success);
+ BOOST_CHECK(!result.request_more);
+ // All headers should be ready for acceptance:
+ BOOST_CHECK(result.pow_validated_headers.size() == first_chain.size());
+ // Nothing left for the sync logic to do:
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+
+ // Finally, verify that just trying to process the second chain would not
+ // succeed (too little work)
+ hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
+ // Pretend just the first message is "full", so we don't abort.
+ (void)hss->ProcessNextHeaders({second_chain.front()}, true);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
+
+ headers_batch.clear();
+ headers_batch.insert(headers_batch.end(), std::next(second_chain.begin(), 1), second_chain.end());
+ // Tell the sync logic that the headers message was not full, implying no
+ // more headers can be requested. For a low-work-chain, this should causes
+ // the sync to end with no headers for acceptance.
+ result = hss->ProcessNextHeaders(headers_batch, false);
+ BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
+ BOOST_CHECK(result.pow_validated_headers.empty());
+ BOOST_CHECK(!result.request_more);
+ // Nevertheless, no validation errors should have been detected with the
+ // chain:
+ BOOST_CHECK(result.success);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index d88aed7d4a..9f5fb17b60 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -588,7 +588,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->nNonce = bi.nonce;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 2f43ae52f7..3695ea9d16 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -20,7 +20,14 @@ BOOST_AUTO_TEST_CASE(get_next_work)
pindexLast.nHeight = 32255;
pindexLast.nTime = 1262152739; // Block #32255
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00d86aU);
+
+ // Here (and below): expected_nbits is calculated in
+ // CalculateNextWorkRequired(); redoing the calculation here would be just
+ // reimplementing the same code that is written in pow.cpp. Rather than
+ // copy that code, we just hardcode the expected result.
+ unsigned int expected_nbits = 0x1d00d86aU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
}
/* Test the constraint on the upper bound for next work */
@@ -32,7 +39,9 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
pindexLast.nHeight = 2015;
pindexLast.nTime = 1233061996; // Block #2015
pindexLast.nBits = 0x1d00ffff;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00ffffU);
+ unsigned int expected_nbits = 0x1d00ffffU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
}
/* Test the constraint on the lower bound for actual time taken */
@@ -44,7 +53,12 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
pindexLast.nHeight = 68543;
pindexLast.nTime = 1279297671; // Block #68543
pindexLast.nBits = 0x1c05a3f4;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1c0168fdU);
+ unsigned int expected_nbits = 0x1c0168fdU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
+ // Test that reducing nbits further would not be a PermittedDifficultyTransition.
+ unsigned int invalid_nbits = expected_nbits-1;
+ BOOST_CHECK(!PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, invalid_nbits));
}
/* Test the constraint on the upper bound for actual time taken */
@@ -56,7 +70,12 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
pindexLast.nHeight = 46367;
pindexLast.nTime = 1269211443; // Block #46367
pindexLast.nBits = 0x1c387f6f;
- BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00e1fdU);
+ unsigned int expected_nbits = 0x1d00e1fdU;
+ BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), expected_nbits);
+ BOOST_CHECK(PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, expected_nbits));
+ // Test that increasing nbits further would not be a PermittedDifficultyTransition.
+ unsigned int invalid_nbits = expected_nbits+1;
+ BOOST_CHECK(!PermittedDifficultyTransition(chainParams->GetConsensus(), pindexLast.nHeight+1, pindexLast.nBits, invalid_nbits));
}
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 3d3fd5d93d..9f5e3ab7ae 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
for (int n=0; n<100; n++) {
int r = InsecureRandRange(150000);
CBlockIndex* tip = (r < 100000) ? &vBlocksMain[r] : &vBlocksSide[r - 100000];
- CBlockLocator locator = chain.GetLocator(tip);
+ CBlockLocator locator = GetLocator(tip);
// The first result must be the block itself, the last one must be genesis.
BOOST_CHECK(locator.vHave.front() == tip->GetBlockHash());
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 88cf9647e7..faa0b2878c 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -68,7 +68,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
assert(block->nNonce);
}
- bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, nullptr)};
+ bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, true, nullptr)};
assert(processed);
return CTxIn{block->vtx[0]->GetHash(), 0};
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 30d26ecf79..8a0b03c9d1 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -321,7 +321,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(
const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr);
+ Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr);
return block;
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 921cd37327..61ceca9837 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -23,6 +23,7 @@
#include <util/string.h>
#include <util/time.h>
#include <util/vector.h>
+#include <util/bitdeque.h>
#include <array>
#include <fstream>
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 1c5ca18759..bb1ade153a 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -100,7 +100,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock>
// submit block header, so that miner can get the block height from the
// global state and the node has the topology of the chain
BlockValidationState ignored;
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, true, ignored));
return pblock;
}
@@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
bool ignored;
// Connect the genesis block and drain any outstanding events
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -179,13 +179,13 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
FastRandomContext insecure;
for (int i = 0; i < 1000; i++) {
auto block = blocks[insecure.randrange(blocks.size() - 1)];
- Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
+ Assert(m_node.chainman)->ProcessNewBlock(block, true, true, &ignored);
}
// to make sure that eventually we process the full chain - do it here
for (const auto& block : blocks) {
if (block->vtx.size() == 1) {
- bool processed = Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
+ bool processed = Assert(m_node.chainman)->ProcessNewBlock(block, true, true, &ignored);
assert(processed);
}
}
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
bool ignored;
auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
- return Assert(m_node.chainman)->ProcessNewBlock(block, /*force_processing=*/true, /*new_block=*/&ignored);
+ return Assert(m_node.chainman)->ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&ignored);
};
// Process all mined blocks
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index ee60f9aa4d..98294b9028 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -132,7 +132,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
BOOST_CHECK(checked);
bool accepted = background_cs.AcceptBlock(
- pblock, state, &pindex, true, nullptr, &newblock);
+ pblock, state, &pindex, true, nullptr, &newblock, true);
BOOST_CHECK(accepted);
}
// UpdateTip is called here