test(headerssync): headers_sync_chainwork test improvements
Introduces CHECK_RESULT for consistently validating ProcessingResult. * Verifies HeadersSyncState::State directly after ProcessNextHeaders(). * Uses BOOST_REQUIRE_EQUAL for HeadersSyncState::State - Nicer failure output and prevents continuing test in nonsensical state. * Encourages checking Locator and result.pow_validated_headers. Changes happy_path to test both full & non-full headers messages.
This commit is contained in:
1 file changed
+75
-32
@@ -15,6 +15,31 @@
|
|||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
using State = HeadersSyncState::State;
|
||||||
|
|
||||||
|
// Standard set of checks common to all scenarios. Macro keeps failure lines at the call-site.
|
||||||
|
#define CHECK_RESULT(result_expression, hss, exp_state, exp_success, exp_request_more, \
|
||||||
|
exp_headers_size, exp_pow_validated_prev, exp_locator_hash) \
|
||||||
|
do { \
|
||||||
|
const auto result{result_expression}; \
|
||||||
|
BOOST_REQUIRE_EQUAL(hss.GetState(), exp_state); \
|
||||||
|
BOOST_CHECK_EQUAL(result.success, exp_success); \
|
||||||
|
BOOST_CHECK_EQUAL(result.request_more, exp_request_more); \
|
||||||
|
BOOST_CHECK_EQUAL(result.pow_validated_headers.size(), exp_headers_size); \
|
||||||
|
const std::optional<uint256> pow_validated_prev_opt{exp_pow_validated_prev}; \
|
||||||
|
if (pow_validated_prev_opt) { \
|
||||||
|
BOOST_CHECK_EQUAL(result.pow_validated_headers.at(0).hashPrevBlock, pow_validated_prev_opt); \
|
||||||
|
} else { \
|
||||||
|
BOOST_CHECK_EQUAL(exp_headers_size, 0); \
|
||||||
|
} \
|
||||||
|
const std::optional<uint256> locator_hash_opt{exp_locator_hash}; \
|
||||||
|
if (locator_hash_opt) { \
|
||||||
|
BOOST_CHECK_EQUAL(hss.NextHeadersRequestLocator().vHave.at(0), locator_hash_opt); \
|
||||||
|
} else { \
|
||||||
|
BOOST_CHECK_EQUAL(exp_state, State::FINAL); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
constexpr size_t TARGET_BLOCKS{15'000};
|
constexpr size_t TARGET_BLOCKS{15'000};
|
||||||
constexpr arith_uint256 CHAIN_WORK{TARGET_BLOCKS * 2};
|
constexpr arith_uint256 CHAIN_WORK{TARGET_BLOCKS * 2};
|
||||||
|
|
||||||
@@ -113,42 +138,56 @@ BOOST_AUTO_TEST_CASE(sneaky_redownload)
|
|||||||
// initially and then the rest.
|
// initially and then the rest.
|
||||||
HeadersSyncState hss{CreateState()};
|
HeadersSyncState hss{CreateState()};
|
||||||
|
|
||||||
// Just feed one header.
|
// Just feed one header and check state.
|
||||||
// Pretend the message is still "full", so we don't abort.
|
// Pretend the message is still "full", so we don't abort.
|
||||||
(void)hss.ProcessNextHeaders({{first_chain.front()}}, true);
|
CHECK_RESULT(hss.ProcessNextHeaders({{first_chain.front()}}, /*full_headers_message=*/true),
|
||||||
|
hss, /*exp_state=*/State::PRESYNC,
|
||||||
auto result{hss.ProcessNextHeaders(std::span{first_chain}.subspan(1), true)};
|
/*exp_success*/true, /*exp_request_more=*/true,
|
||||||
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/first_chain.front().GetHash());
|
||||||
|
|
||||||
// This chain should look valid, and we should have met the proof-of-work
|
// This chain should look valid, and we should have met the proof-of-work
|
||||||
// requirement during PRESYNC and transitioned to REDOWNLOAD.
|
// requirement during PRESYNC and transitioned to REDOWNLOAD.
|
||||||
BOOST_CHECK(result.success);
|
CHECK_RESULT(hss.ProcessNextHeaders(std::span{first_chain}.subspan(1), true),
|
||||||
BOOST_CHECK(result.request_more);
|
hss, /*exp_state=*/State::REDOWNLOAD,
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::REDOWNLOAD);
|
/*exp_success*/true, /*exp_request_more=*/true,
|
||||||
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/genesis.GetHash());
|
||||||
|
|
||||||
// Try to sneakily feed back the second chain during REDOWNLOAD.
|
// Try to sneakily feed back the second chain during REDOWNLOAD.
|
||||||
result = hss.ProcessNextHeaders(second_chain, true);
|
CHECK_RESULT(hss.ProcessNextHeaders(second_chain, true),
|
||||||
BOOST_CHECK(!result.success); // foiled!
|
hss, /*exp_state=*/State::FINAL,
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::FINAL);
|
/*exp_success*/false, // Foiled! We detected mismatching headers.
|
||||||
|
/*exp_request_more=*/false,
|
||||||
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(happy_path)
|
BOOST_AUTO_TEST_CASE(happy_path)
|
||||||
{
|
{
|
||||||
const auto& first_chain{FirstChain()};
|
const auto& first_chain{FirstChain()};
|
||||||
|
|
||||||
// This time we feed the first chain twice.
|
// Headers message that moves us to the next state doesn't need to be full.
|
||||||
HeadersSyncState hss{CreateState()};
|
for (const bool full_headers_message : {false, true}) {
|
||||||
|
// This time we feed the first chain twice.
|
||||||
|
HeadersSyncState hss{CreateState()};
|
||||||
|
|
||||||
// Sufficient work transitions us from PRESYNC to REDOWNLOAD:
|
// Sufficient work transitions us from PRESYNC to REDOWNLOAD:
|
||||||
(void)hss.ProcessNextHeaders(first_chain, true);
|
const auto genesis_hash{genesis.GetHash()};
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::REDOWNLOAD);
|
CHECK_RESULT(hss.ProcessNextHeaders(first_chain, full_headers_message),
|
||||||
|
hss, /*exp_state=*/State::REDOWNLOAD,
|
||||||
|
/*exp_success*/true, /*exp_request_more=*/true,
|
||||||
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/genesis_hash);
|
||||||
|
|
||||||
const auto result{hss.ProcessNextHeaders(first_chain, true)};
|
CHECK_RESULT(hss.ProcessNextHeaders(first_chain, full_headers_message),
|
||||||
BOOST_CHECK(result.success);
|
// Nothing left for the sync logic to do:
|
||||||
BOOST_CHECK(!result.request_more);
|
hss, /*exp_state=*/State::FINAL,
|
||||||
// All headers should be ready for acceptance:
|
/*exp_success*/true, /*exp_request_more=*/false,
|
||||||
BOOST_CHECK(result.pow_validated_headers.size() == first_chain.size());
|
// All headers except the one already returned above:
|
||||||
// Nothing left for the sync logic to do:
|
/*exp_headers_size=*/first_chain.size(), /*exp_pow_validated_prev=*/genesis_hash,
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::FINAL);
|
/*exp_locator_hash=*/std::nullopt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(too_little_work)
|
BOOST_AUTO_TEST_CASE(too_little_work)
|
||||||
@@ -158,22 +197,26 @@ BOOST_AUTO_TEST_CASE(too_little_work)
|
|||||||
// Verify that just trying to process the second chain would not succeed
|
// Verify that just trying to process the second chain would not succeed
|
||||||
// (too little work).
|
// (too little work).
|
||||||
HeadersSyncState hss{CreateState()};
|
HeadersSyncState hss{CreateState()};
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::PRESYNC);
|
BOOST_REQUIRE_EQUAL(hss.GetState(), State::PRESYNC);
|
||||||
|
|
||||||
// Pretend just the first message is "full", so we don't abort.
|
// Pretend just the first message is "full", so we don't abort.
|
||||||
(void)hss.ProcessNextHeaders({{second_chain.front()}}, true);
|
CHECK_RESULT(hss.ProcessNextHeaders({{second_chain.front()}}, true),
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::PRESYNC);
|
hss, /*exp_state=*/State::PRESYNC,
|
||||||
|
/*exp_success*/true, /*exp_request_more=*/true,
|
||||||
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/second_chain.front().GetHash());
|
||||||
|
|
||||||
// Tell the sync logic that the headers message was not full, implying no
|
// 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 cause
|
// more headers can be requested. For a low-work-chain, this should cause
|
||||||
// the sync to end with no headers for acceptance.
|
// the sync to end with no headers for acceptance.
|
||||||
const auto result{hss.ProcessNextHeaders(std::span{second_chain}.subspan(1), false)};
|
CHECK_RESULT(hss.ProcessNextHeaders(std::span{second_chain}.subspan(1), false),
|
||||||
BOOST_CHECK(hss.GetState() == HeadersSyncState::State::FINAL);
|
hss, /*exp_state=*/State::FINAL,
|
||||||
BOOST_CHECK(result.pow_validated_headers.empty());
|
// Nevertheless, no validation errors should have been detected with the
|
||||||
BOOST_CHECK(!result.request_more);
|
// chain:
|
||||||
// Nevertheless, no validation errors should have been detected with the
|
/*exp_success*/true,
|
||||||
// chain:
|
/*exp_request_more=*/false,
|
||||||
BOOST_CHECK(result.success);
|
/*exp_headers_size=*/0, /*exp_pow_validated_prev=*/std::nullopt,
|
||||||
|
/*exp_locator_hash=*/std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
Reference in new issue
Block a user