Bitcoin Core
31.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
test
headers_sync_chainwork_tests.cpp
Go to the documentation of this file.
1
// Copyright (c) 2022-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <chain.h>
6
#include <chainparams.h>
7
#include <
consensus/params.h
>
8
#include <
headerssync.h
>
9
#include <
net_processing.h
>
10
#include <
pow.h
>
11
#include <
test/util/common.h
>
12
#include <
test/util/setup_common.h
>
13
#include <validation.h>
14
15
#include <cstddef>
16
#include <vector>
17
18
#include <boost/test/unit_test.hpp>
19
20
using
State
=
HeadersSyncState::State
;
21
22
// Standard set of checks common to all scenarios. Macro keeps failure lines at the call-site.
23
#define CHECK_RESULT(result_expression, hss, exp_state, exp_success, exp_request_more, \
24
exp_headers_size, exp_pow_validated_prev, exp_locator_hash) \
25
do { \
26
const auto result{result_expression}; \
27
BOOST_REQUIRE_EQUAL(hss.GetState(), exp_state); \
28
BOOST_CHECK_EQUAL(result.success, exp_success); \
29
BOOST_CHECK_EQUAL(result.request_more, exp_request_more); \
30
BOOST_CHECK_EQUAL(result.pow_validated_headers.size(), exp_headers_size); \
31
const std::optional<uint256> pow_validated_prev_opt{exp_pow_validated_prev}; \
32
if (pow_validated_prev_opt) { \
33
BOOST_CHECK_EQUAL(result.pow_validated_headers.at(0).hashPrevBlock, pow_validated_prev_opt); \
34
} else { \
35
BOOST_CHECK_EQUAL(exp_headers_size, 0); \
36
} \
37
const std::optional<uint256> locator_hash_opt{exp_locator_hash}; \
38
if (locator_hash_opt) { \
39
BOOST_CHECK_EQUAL(hss.NextHeadersRequestLocator().vHave.at(0), locator_hash_opt); \
40
} else { \
41
BOOST_CHECK_EQUAL(exp_state, State::FINAL); \
42
} \
43
} while (false)
44
45
constexpr
size_t
TARGET_BLOCKS
{15'000};
46
constexpr
arith_uint256
CHAIN_WORK
{
TARGET_BLOCKS
* 2};
47
48
// Subtract MAX_HEADERS_RESULTS (2000 headers/message) + an arbitrary smaller
49
// value (123) so our redownload buffer is well below the number of blocks
50
// required to reach the CHAIN_WORK threshold, to behave similarly to mainnet.
51
constexpr
size_t
REDOWNLOAD_BUFFER_SIZE
{
TARGET_BLOCKS
- (
MAX_HEADERS_RESULTS
+ 123)};
52
constexpr
size_t
COMMITMENT_PERIOD
{600};
// Somewhat close to mainnet.
53
54
struct
HeadersGeneratorSetup
:
public
RegTestingSetup
{
55
const
CBlock
&
genesis
{
Params
().
GenesisBlock
()};
56
CBlockIndex
&
chain_start
{
WITH_LOCK
(
::cs_main
,
return
*
Assert
(
m_node
.chainman->m_blockman.LookupBlockIndex(
genesis
.GetHash())))};
57
58
// Generate headers for two different chains (using differing merkle roots
59
// to ensure the headers are different).
60
const
std::vector<CBlockHeader>&
FirstChain
()
61
{
62
// Block header hash target is half of max uint256 (2**256 / 2), expressible
63
// roughly as the coefficient 0x7fffff with the exponent 0x20 (32 bytes).
64
// This implies around every 2nd hash attempt should succeed, which
65
// is why CHAIN_WORK == TARGET_BLOCKS * 2.
66
assert
(
genesis
.nBits == 0x207fffff);
67
68
// Subtract 1 since the genesis block also contributes work so we reach
69
// the CHAIN_WORK target.
70
static
const
auto
first_chain{
GenerateHeaders
(
/*count=*/
TARGET_BLOCKS
- 1,
genesis
.GetHash(),
71
genesis
.nVersion,
genesis
.nTime,
/*merkle_root=*/
uint256::ZERO
,
genesis
.nBits)};
72
return
first_chain;
73
}
74
const
std::vector<CBlockHeader>&
SecondChain
()
75
{
76
// Subtract 2 to keep total work below the target.
77
static
const
auto
second_chain{
GenerateHeaders
(
/*count=*/
TARGET_BLOCKS
- 2,
genesis
.GetHash(),
78
genesis
.nVersion,
genesis
.nTime,
/*merkle_root=*/
uint256::ONE
,
genesis
.nBits)};
79
return
second_chain;
80
}
81
82
HeadersSyncState
CreateState
()
83
{
84
return
{
/*id=*/
0,
85
Params
().
GetConsensus
(),
86
HeadersSyncParams
{
87
.commitment_period =
COMMITMENT_PERIOD
,
88
.redownload_buffer_size =
REDOWNLOAD_BUFFER_SIZE
,
89
},
90
chain_start
,
91
/*minimum_required_work=*/
CHAIN_WORK
};
92
}
93
94
private
:
96
void
FindProofOfWork
(
CBlockHeader
& starting_header);
102
std::vector<CBlockHeader>
GenerateHeaders
(
size_t
count
,
103
uint256
prev_hash, int32_t nVersion, uint32_t prev_time,
104
const
uint256
& merkle_root, uint32_t nBits);
105
};
106
107
void
HeadersGeneratorSetup::FindProofOfWork
(
CBlockHeader
& starting_header)
108
{
109
while
(!
CheckProofOfWork
(starting_header.
GetHash
(), starting_header.
nBits
,
Params
().GetConsensus())) {
110
++starting_header.
nNonce
;
111
}
112
}
113
114
std::vector<CBlockHeader>
HeadersGeneratorSetup::GenerateHeaders
(
115
const
size_t
count
,
uint256
prev_hash,
const
int32_t nVersion,
116
uint32_t prev_time,
const
uint256
& merkle_root,
const
uint32_t nBits)
117
{
118
std::vector<CBlockHeader> headers(
count
);
119
for
(
auto
& next_header : headers) {
120
next_header.nVersion = nVersion;
121
next_header.hashPrevBlock = prev_hash;
122
next_header.hashMerkleRoot = merkle_root;
123
next_header.nTime = ++prev_time;
124
next_header.nBits = nBits;
125
126
FindProofOfWork
(next_header);
127
prev_hash = next_header.GetHash();
128
}
129
return
headers;
130
}
131
132
// In this test, we construct two sets of headers from genesis, one with
133
// sufficient proof of work and one without.
134
// 1. We deliver the first set of headers and verify that the headers sync state
135
// updates to the REDOWNLOAD phase successfully.
136
// Then we deliver the second set of headers and verify that they fail
137
// processing (presumably due to commitments not matching).
138
// 2. Verify that repeating with the first set of headers in both phases is
139
// successful.
140
// 3. Repeat the second set of headers in both phases to demonstrate behavior
141
// when the chain a peer provides has too little work.
142
BOOST_FIXTURE_TEST_SUITE
(headers_sync_chainwork_tests,
HeadersGeneratorSetup
)
143
144
BOOST_AUTO_TEST_CASE
(sneaky_redownload)
145
{
146
const
auto
& first_chain{FirstChain()};
147
const
auto
& second_chain{SecondChain()};
148
149
// Feed the first chain to HeadersSyncState, by delivering 1 header
150
// initially and then the rest.
151
HeadersSyncState
hss{CreateState()};
152
153
// Just feed one header and check state.
154
// Pretend the message is still "full", so we don't abort.
155
CHECK_RESULT
(hss.
ProcessNextHeaders
({{first_chain.front()}},
/*full_headers_message=*/
true
),
156
hss,
/*exp_state=*/
State::PRESYNC,
157
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
158
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
159
/*exp_locator_hash=*/
first_chain.front().GetHash());
160
161
// This chain should look valid, and we should have met the proof-of-work
162
// requirement during PRESYNC and transitioned to REDOWNLOAD.
163
CHECK_RESULT
(hss.ProcessNextHeaders(std::span{first_chain}.subspan(1),
true
),
164
hss,
/*exp_state=*/
State::REDOWNLOAD,
165
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
166
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
167
/*exp_locator_hash=*/
genesis.GetHash());
168
169
// Below is the number of commitment bits that must randomly match between
170
// the two chains for this test to spuriously fail. 1 / 2^25 =
171
// 1 in 33'554'432 (somewhat less due to HeadersSyncState::m_commit_offset).
172
static_assert
(
TARGET_BLOCKS
/
COMMITMENT_PERIOD
== 25);
173
174
// Try to sneakily feed back the second chain during REDOWNLOAD.
175
CHECK_RESULT
(hss.ProcessNextHeaders(second_chain,
true
),
176
hss,
/*exp_state=*/
State::FINAL,
177
/*exp_success=*/
false
,
// Foiled! We detected mismatching headers.
178
/*exp_request_more=*/
false
,
179
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
180
/*exp_locator_hash=*/
std::nullopt);
181
}
182
183
BOOST_AUTO_TEST_CASE
(happy_path)
184
{
185
const
auto
& first_chain{FirstChain()};
186
187
// Headers message that moves us to the next state doesn't need to be full.
188
for
(
const
bool
full_headers_message : {
false
,
true
}) {
189
// This time we feed the first chain twice.
190
HeadersSyncState
hss{CreateState()};
191
192
// Sufficient work transitions us from PRESYNC to REDOWNLOAD:
193
const
auto
genesis_hash{genesis.GetHash()};
194
CHECK_RESULT
(hss.
ProcessNextHeaders
(first_chain, full_headers_message),
195
hss,
/*exp_state=*/
State::REDOWNLOAD,
196
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
197
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
198
/*exp_locator_hash=*/
genesis_hash);
199
200
// Process only so that the internal threshold isn't exceeded, meaning
201
// validated headers shouldn't be returned yet:
202
CHECK_RESULT
(hss.
ProcessNextHeaders
({first_chain.begin(), REDOWNLOAD_BUFFER_SIZE},
true
),
203
hss,
/*exp_state=*/
State::REDOWNLOAD,
204
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
205
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
206
/*exp_locator_hash=*/
first_chain[
REDOWNLOAD_BUFFER_SIZE
- 1].GetHash());
207
208
// We start receiving headers for permanent storage before completing:
209
CHECK_RESULT
(hss.
ProcessNextHeaders
({{first_chain[REDOWNLOAD_BUFFER_SIZE]}},
true
),
210
hss,
/*exp_state=*/
State::REDOWNLOAD,
211
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
212
/*exp_headers_size=*/
1,
/*exp_pow_validated_prev=*/
genesis_hash,
213
/*exp_locator_hash=*/
first_chain[
REDOWNLOAD_BUFFER_SIZE
].GetHash());
214
215
// Feed in remaining headers, meeting the work threshold again and
216
// completing the REDOWNLOAD phase:
217
CHECK_RESULT
(hss.ProcessNextHeaders({first_chain.begin() + REDOWNLOAD_BUFFER_SIZE + 1, first_chain.end()}, full_headers_message),
218
hss,
/*exp_state=*/
State::FINAL,
219
/*exp_success=*/
true
,
/*exp_request_more=*/
false
,
220
// All headers except the one already returned above:
221
/*exp_headers_size=*/
first_chain.size() - 1,
/*exp_pow_validated_prev=*/
first_chain.front().GetHash(),
222
/*exp_locator_hash=*/
std::nullopt);
223
}
224
}
225
226
BOOST_AUTO_TEST_CASE
(too_little_work)
227
{
228
const
auto
& second_chain{SecondChain()};
229
230
// Verify that just trying to process the second chain would not succeed
231
// (too little work).
232
HeadersSyncState
hss{CreateState()};
233
BOOST_REQUIRE_EQUAL(hss.
GetState
(), State::PRESYNC);
234
235
// Pretend just the first message is "full", so we don't abort.
236
CHECK_RESULT
(hss.
ProcessNextHeaders
({{second_chain.front()}},
true
),
237
hss,
/*exp_state=*/
State::PRESYNC,
238
/*exp_success=*/
true
,
/*exp_request_more=*/
true
,
239
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
240
/*exp_locator_hash=*/
second_chain.front().GetHash());
241
242
// Tell the sync logic that the headers message was not full, implying no
243
// more headers can be requested. For a low-work-chain, this should cause
244
// the sync to end with no headers for acceptance.
245
CHECK_RESULT
(hss.ProcessNextHeaders(std::span{second_chain}.subspan(1),
false
),
246
hss,
/*exp_state=*/
State::FINAL,
247
// Nevertheless, no validation errors should have been detected with the
248
// chain:
249
/*exp_success=*/
true
,
250
/*exp_request_more=*/
false
,
251
/*exp_headers_size=*/
0,
/*exp_pow_validated_prev=*/
std::nullopt,
252
/*exp_locator_hash=*/
std::nullopt);
253
}
254
255
BOOST_AUTO_TEST_SUITE_END
()
Params
const CChainParams & Params()
Return the currently selected parameters.
Definition
chainparams.cpp:112
Assert
#define Assert(val)
Identity function.
Definition
check.h:113
CBlockHeader
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition
block.h:27
CBlockHeader::nNonce
uint32_t nNonce
Definition
block.h:35
CBlockHeader::nBits
uint32_t nBits
Definition
block.h:34
CBlockHeader::GetHash
uint256 GetHash() const
Definition
block.cpp:15
CBlock
Definition
block.h:74
CBlockIndex
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition
chain.h:94
CChainParams::GenesisBlock
const CBlock & GenesisBlock() const
Definition
chainparams.h:94
CChainParams::GetConsensus
const Consensus::Params & GetConsensus() const
Definition
chainparams.h:89
HeadersSyncState
HeadersSyncState:
Definition
headerssync.h:102
HeadersSyncState::State
State
Definition
headerssync.h:106
HeadersSyncState::ProcessNextHeaders
ProcessingResult ProcessNextHeaders(std::span< const CBlockHeader > received_headers, bool full_headers_message)
Process a batch of headers, once a sync via this mechanism has started.
Definition
headerssync.cpp:68
HeadersSyncState::GetState
State GetState() const
Return the current state of our download.
Definition
headerssync.h:120
arith_uint256
256-bit unsigned big integer.
Definition
arith_uint256.h:231
uint256
256-bit opaque blob.
Definition
uint256.h:195
uint256::ONE
static const uint256 ONE
Definition
uint256.h:204
uint256::ZERO
static const uint256 ZERO
Definition
uint256.h:203
cs_main
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition
cs_main.cpp:8
BOOST_FIXTURE_TEST_SUITE
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
REDOWNLOAD_BUFFER_SIZE
constexpr size_t REDOWNLOAD_BUFFER_SIZE
Definition
headers_sync_chainwork_tests.cpp:51
TARGET_BLOCKS
constexpr size_t TARGET_BLOCKS
Definition
headers_sync_chainwork_tests.cpp:45
CHECK_RESULT
#define CHECK_RESULT(result_expression, hss, exp_state, exp_success, exp_request_more, exp_headers_size, exp_pow_validated_prev, exp_locator_hash)
Definition
headers_sync_chainwork_tests.cpp:23
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(sneaky_redownload)
Definition
headers_sync_chainwork_tests.cpp:144
COMMITMENT_PERIOD
constexpr size_t COMMITMENT_PERIOD
Definition
headers_sync_chainwork_tests.cpp:52
CHAIN_WORK
constexpr arith_uint256 CHAIN_WORK
Definition
headers_sync_chainwork_tests.cpp:46
State
HeadersSyncState::State State
Definition
headers_sync_chainwork_tests.cpp:20
headerssync.h
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(ipc_tests)
Definition
ipc_tests.cpp:13
net_processing.h
MAX_HEADERS_RESULTS
static const unsigned int MAX_HEADERS_RESULTS
Number of headers sent in one getheaders result.
Definition
net_processing.h:51
params.h
CheckProofOfWork
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params ¶ms)
Check whether a block hash satisfies the proof-of-work requirement specified by nBits.
Definition
pow.cpp:140
pow.h
setup_common.h
BasicTestingSetup::m_node
node::NodeContext m_node
Definition
setup_common.h:66
HeadersGeneratorSetup
Definition
headers_sync_chainwork_tests.cpp:54
HeadersGeneratorSetup::FindProofOfWork
void FindProofOfWork(CBlockHeader &starting_header)
Search for a nonce to meet (regtest) proof of work.
Definition
headers_sync_chainwork_tests.cpp:107
HeadersGeneratorSetup::GenerateHeaders
std::vector< CBlockHeader > GenerateHeaders(size_t count, uint256 prev_hash, int32_t nVersion, uint32_t prev_time, const uint256 &merkle_root, uint32_t nBits)
Generate headers in a chain that build off a given starting hash, using the given nVersion,...
Definition
headers_sync_chainwork_tests.cpp:114
HeadersGeneratorSetup::CreateState
HeadersSyncState CreateState()
Definition
headers_sync_chainwork_tests.cpp:82
HeadersGeneratorSetup::genesis
const CBlock & genesis
Definition
headers_sync_chainwork_tests.cpp:55
HeadersGeneratorSetup::chain_start
CBlockIndex & chain_start
Definition
headers_sync_chainwork_tests.cpp:56
HeadersGeneratorSetup::SecondChain
const std::vector< CBlockHeader > & SecondChain()
Definition
headers_sync_chainwork_tests.cpp:74
HeadersGeneratorSetup::FirstChain
const std::vector< CBlockHeader > & FirstChain()
Definition
headers_sync_chainwork_tests.cpp:60
HeadersSyncParams
Configuration for headers sync memory usage.
Definition
chainparams.h:64
RegTestingSetup::RegTestingSetup
RegTestingSetup()
Definition
setup_common.h:129
WITH_LOCK
#define WITH_LOCK(cs, code)
Definition
sync.h:289
common.h
count
static int count
Definition
tests_exhaustive.c:34
assert
assert(!tx.IsCoinBase())
Generated on
for Bitcoin Core by
1.17.0