Bitcoin Core 22.99.0
P2P Digital Currency
dbwrapper_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2012-2020 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 <dbwrapper.h>
7#include <uint256.h>
8
9#include <memory>
10
11#include <boost/test/unit_test.hpp>
12
13// Test if a string consists entirely of null characters
14static bool is_null_key(const std::vector<unsigned char>& key) {
15 bool isnull = true;
16
17 for (unsigned int i = 0; i < key.size(); i++)
18 isnull &= (key[i] == '\x00');
19
20 return isnull;
21}
22
24
26{
27 // Perform tests both obfuscated and non-obfuscated.
28 for (const bool obfuscate : {false, true}) {
29 fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
30 CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
31 uint8_t key{'k'};
33 uint256 res;
34
35 // Ensure that we're doing real obfuscation when obfuscate=true
37
38 BOOST_CHECK(dbw.Write(key, in));
39 BOOST_CHECK(dbw.Read(key, res));
41 }
42}
43
44BOOST_AUTO_TEST_CASE(dbwrapper_basic_data)
45{
46 // Perform tests both obfuscated and non-obfuscated.
47 for (bool obfuscate : {false, true}) {
48 fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false");
49 CDBWrapper dbw(ph, (1 << 20), false, true, obfuscate);
50
51 uint256 res;
52 uint32_t res_uint_32;
53 bool res_bool;
54
55 // Ensure that we're doing real obfuscation when obfuscate=true
57
58 //Simulate block raw data - "b + block hash"
59 std::string key_block = "b" + InsecureRand256().ToString();
60
61 uint256 in_block = InsecureRand256();
62 BOOST_CHECK(dbw.Write(key_block, in_block));
63 BOOST_CHECK(dbw.Read(key_block, res));
64 BOOST_CHECK_EQUAL(res.ToString(), in_block.ToString());
65
66 //Simulate file raw data - "f + file_number"
67 std::string key_file = strprintf("f%04x", InsecureRand32());
68
69 uint256 in_file_info = InsecureRand256();
70 BOOST_CHECK(dbw.Write(key_file, in_file_info));
71 BOOST_CHECK(dbw.Read(key_file, res));
72 BOOST_CHECK_EQUAL(res.ToString(), in_file_info.ToString());
73
74 //Simulate transaction raw data - "t + transaction hash"
75 std::string key_transaction = "t" + InsecureRand256().ToString();
76
77 uint256 in_transaction = InsecureRand256();
78 BOOST_CHECK(dbw.Write(key_transaction, in_transaction));
79 BOOST_CHECK(dbw.Read(key_transaction, res));
80 BOOST_CHECK_EQUAL(res.ToString(), in_transaction.ToString());
81
82 //Simulate UTXO raw data - "c + transaction hash"
83 std::string key_utxo = "c" + InsecureRand256().ToString();
84
85 uint256 in_utxo = InsecureRand256();
86 BOOST_CHECK(dbw.Write(key_utxo, in_utxo));
87 BOOST_CHECK(dbw.Read(key_utxo, res));
88 BOOST_CHECK_EQUAL(res.ToString(), in_utxo.ToString());
89
90 //Simulate last block file number - "l"
91 uint8_t key_last_blockfile_number{'l'};
92 uint32_t lastblockfilenumber = InsecureRand32();
93 BOOST_CHECK(dbw.Write(key_last_blockfile_number, lastblockfilenumber));
94 BOOST_CHECK(dbw.Read(key_last_blockfile_number, res_uint_32));
95 BOOST_CHECK_EQUAL(lastblockfilenumber, res_uint_32);
96
97 //Simulate Is Reindexing - "R"
98 uint8_t key_IsReindexing{'R'};
99 bool isInReindexing = InsecureRandBool();
100 BOOST_CHECK(dbw.Write(key_IsReindexing, isInReindexing));
101 BOOST_CHECK(dbw.Read(key_IsReindexing, res_bool));
102 BOOST_CHECK_EQUAL(isInReindexing, res_bool);
103
104 //Simulate last block hash up to which UXTO covers - 'B'
105 uint8_t key_lastblockhash_uxto{'B'};
106 uint256 lastblock_hash = InsecureRand256();
107 BOOST_CHECK(dbw.Write(key_lastblockhash_uxto, lastblock_hash));
108 BOOST_CHECK(dbw.Read(key_lastblockhash_uxto, res));
109 BOOST_CHECK_EQUAL(lastblock_hash, res);
110
111 //Simulate file raw data - "F + filename_number + filename"
112 std::string file_option_tag = "F";
113 uint8_t filename_length = InsecureRandBits(8);
114 std::string filename = "randomfilename";
115 std::string key_file_option = strprintf("%s%01x%s", file_option_tag,filename_length,filename);
116
117 bool in_file_bool = InsecureRandBool();
118 BOOST_CHECK(dbw.Write(key_file_option, in_file_bool));
119 BOOST_CHECK(dbw.Read(key_file_option, res_bool));
120 BOOST_CHECK_EQUAL(res_bool, in_file_bool);
121 }
122}
123
124// Test batch operations
125BOOST_AUTO_TEST_CASE(dbwrapper_batch)
126{
127 // Perform tests both obfuscated and non-obfuscated.
128 for (const bool obfuscate : {false, true}) {
129 fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
130 CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
131
132 uint8_t key{'i'};
134 uint8_t key2{'j'};
135 uint256 in2 = InsecureRand256();
136 uint8_t key3{'k'};
137 uint256 in3 = InsecureRand256();
138
139 uint256 res;
140 CDBBatch batch(dbw);
141
142 batch.Write(key, in);
143 batch.Write(key2, in2);
144 batch.Write(key3, in3);
145
146 // Remove key3 before it's even been written
147 batch.Erase(key3);
148
149 BOOST_CHECK(dbw.WriteBatch(batch));
150
151 BOOST_CHECK(dbw.Read(key, res));
153 BOOST_CHECK(dbw.Read(key2, res));
154 BOOST_CHECK_EQUAL(res.ToString(), in2.ToString());
155
156 // key3 should've never been written
157 BOOST_CHECK(dbw.Read(key3, res) == false);
158 }
159}
160
161BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
162{
163 // Perform tests both obfuscated and non-obfuscated.
164 for (const bool obfuscate : {false, true}) {
165 fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
166 CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
167
168 // The two keys are intentionally chosen for ordering
169 uint8_t key{'j'};
171 BOOST_CHECK(dbw.Write(key, in));
172 uint8_t key2{'k'};
173 uint256 in2 = InsecureRand256();
174 BOOST_CHECK(dbw.Write(key2, in2));
175
176 std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
177
178 // Be sure to seek past the obfuscation key (if it exists)
179 it->Seek(key);
180
181 uint8_t key_res;
182 uint256 val_res;
183
184 BOOST_REQUIRE(it->GetKey(key_res));
185 BOOST_REQUIRE(it->GetValue(val_res));
186 BOOST_CHECK_EQUAL(key_res, key);
187 BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());
188
189 it->Next();
190
191 BOOST_REQUIRE(it->GetKey(key_res));
192 BOOST_REQUIRE(it->GetValue(val_res));
193 BOOST_CHECK_EQUAL(key_res, key2);
194 BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());
195
196 it->Next();
197 BOOST_CHECK_EQUAL(it->Valid(), false);
198 }
199}
200
201// Test that we do not obfuscation if there is existing data.
202BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
203{
204 // We're going to share this fs::path between two wrappers
205 fs::path ph = m_args.GetDataDirBase() / "existing_data_no_obfuscate";
206 create_directories(ph);
207
208 // Set up a non-obfuscated wrapper to write some initial data.
209 std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
210 uint8_t key{'k'};
212 uint256 res;
213
214 BOOST_CHECK(dbw->Write(key, in));
215 BOOST_CHECK(dbw->Read(key, res));
217
218 // Call the destructor to free leveldb LOCK
219 dbw.reset();
220
221 // Now, set up another wrapper that wants to obfuscate the same directory
222 CDBWrapper odbw(ph, (1 << 10), false, false, true);
223
224 // Check that the key/val we wrote with unobfuscated wrapper exists and
225 // is readable.
226 uint256 res2;
227 BOOST_CHECK(odbw.Read(key, res2));
228 BOOST_CHECK_EQUAL(res2.ToString(), in.ToString());
229
230 BOOST_CHECK(!odbw.IsEmpty()); // There should be existing data
231 BOOST_CHECK(is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); // The key should be an empty string
232
233 uint256 in2 = InsecureRand256();
234 uint256 res3;
235
236 // Check that we can write successfully
237 BOOST_CHECK(odbw.Write(key, in2));
238 BOOST_CHECK(odbw.Read(key, res3));
239 BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString());
240}
241
242// Ensure that we start obfuscating during a reindex.
243BOOST_AUTO_TEST_CASE(existing_data_reindex)
244{
245 // We're going to share this fs::path between two wrappers
246 fs::path ph = m_args.GetDataDirBase() / "existing_data_reindex";
247 create_directories(ph);
248
249 // Set up a non-obfuscated wrapper to write some initial data.
250 std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
251 uint8_t key{'k'};
253 uint256 res;
254
255 BOOST_CHECK(dbw->Write(key, in));
256 BOOST_CHECK(dbw->Read(key, res));
258
259 // Call the destructor to free leveldb LOCK
260 dbw.reset();
261
262 // Simulate a -reindex by wiping the existing data store
263 CDBWrapper odbw(ph, (1 << 10), false, true, true);
264
265 // Check that the key/val we wrote with unobfuscated wrapper doesn't exist
266 uint256 res2;
267 BOOST_CHECK(!odbw.Read(key, res2));
269
270 uint256 in2 = InsecureRand256();
271 uint256 res3;
272
273 // Check that we can write successfully
274 BOOST_CHECK(odbw.Write(key, in2));
275 BOOST_CHECK(odbw.Read(key, res3));
276 BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString());
277}
278
279BOOST_AUTO_TEST_CASE(iterator_ordering)
280{
281 fs::path ph = m_args.GetDataDirBase() / "iterator_ordering";
282 CDBWrapper dbw(ph, (1 << 20), true, false, false);
283 for (int x=0x00; x<256; ++x) {
284 uint8_t key = x;
285 uint32_t value = x*x;
286 if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value));
287 }
288
289 // Check that creating an iterator creates a snapshot
290 std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
291
292 for (unsigned int x=0x00; x<256; ++x) {
293 uint8_t key = x;
294 uint32_t value = x*x;
295 if (x & 1) BOOST_CHECK(dbw.Write(key, value));
296 }
297
298 for (const int seek_start : {0x00, 0x80}) {
299 it->Seek((uint8_t)seek_start);
300 for (unsigned int x=seek_start; x<255; ++x) {
301 uint8_t key;
302 uint32_t value;
303 BOOST_CHECK(it->Valid());
304 if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
305 break;
306 BOOST_CHECK(it->GetKey(key));
307 if (x & 1) {
308 BOOST_CHECK_EQUAL(key, x + 1);
309 continue;
310 }
311 BOOST_CHECK(it->GetValue(value));
312 BOOST_CHECK_EQUAL(key, x);
313 BOOST_CHECK_EQUAL(value, x*x);
314 it->Next();
315 }
316 BOOST_CHECK(!it->Valid());
317 }
318}
319
321 // Used to make two serialized objects the same while letting them have different lengths
322 // This is a terrible idea
323 std::string str;
325 explicit StringContentsSerializer(const std::string& inp) : str(inp) {}
326
327 StringContentsSerializer& operator+=(const std::string& s) {
328 str += s;
329 return *this;
330 }
332
333 template<typename Stream>
334 void Serialize(Stream& s) const
335 {
336 for (size_t i = 0; i < str.size(); i++) {
337 s << uint8_t(str[i]);
338 }
339 }
340
341 template<typename Stream>
342 void Unserialize(Stream& s)
343 {
344 str.clear();
345 uint8_t c{0};
346 while (true) {
347 try {
348 s >> c;
349 str.push_back(c);
350 } catch (const std::ios_base::failure&) {
351 break;
352 }
353 }
354 }
355};
356
357BOOST_AUTO_TEST_CASE(iterator_string_ordering)
358{
359 char buf[10];
360
361 fs::path ph = m_args.GetDataDirBase() / "iterator_string_ordering";
362 CDBWrapper dbw(ph, (1 << 20), true, false, false);
363 for (int x=0x00; x<10; ++x) {
364 for (int y = 0; y < 10; y++) {
365 snprintf(buf, sizeof(buf), "%d", x);
367 for (int z = 0; z < y; z++)
368 key += key;
369 uint32_t value = x*x;
370 BOOST_CHECK(dbw.Write(key, value));
371 }
372 }
373
374 std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
375 for (const int seek_start : {0, 5}) {
376 snprintf(buf, sizeof(buf), "%d", seek_start);
377 StringContentsSerializer seek_key(buf);
378 it->Seek(seek_key);
379 for (unsigned int x=seek_start; x<10; ++x) {
380 for (int y = 0; y < 10; y++) {
381 snprintf(buf, sizeof(buf), "%d", x);
382 std::string exp_key(buf);
383 for (int z = 0; z < y; z++)
384 exp_key += exp_key;
386 uint32_t value;
387 BOOST_CHECK(it->Valid());
388 if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
389 break;
390 BOOST_CHECK(it->GetKey(key));
391 BOOST_CHECK(it->GetValue(value));
392 BOOST_CHECK_EQUAL(key.str, exp_key);
393 BOOST_CHECK_EQUAL(value, x*x);
394 it->Next();
395 }
396 }
397 BOOST_CHECK(!it->Valid());
398 }
399}
400
402{
403 // Attempt to create a database with a UTF8 character in the path.
404 // On Windows this test will fail if the directory is created using
405 // the ANSI CreateDirectoryA call and the code page isn't UTF8.
406 // It will succeed if created with CreateDirectoryW.
407 fs::path ph = m_args.GetDataDirBase() / "test_runner_₿_🏃_20191128_104644";
408 CDBWrapper dbw(ph, (1 << 20));
409
410 fs::path lockPath = ph / "LOCK";
411 BOOST_CHECK(fs::exists(lockPath));
412}
413
414
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:48
void Erase(const K &key)
Definition: dbwrapper.h:98
void Write(const K &key, const V &value)
Definition: dbwrapper.h:73
bool WriteBatch(CDBBatch &batch, bool fSync=false)
Definition: dbwrapper.cpp:183
bool Read(const K &key, V &value) const
Definition: dbwrapper.h:231
bool Write(const K &key, const V &value, bool fSync=false)
Definition: dbwrapper.h:257
bool IsEmpty()
Return true if the database managed by this class contains no entries.
Definition: dbwrapper.cpp:230
std::string ToString() const
Definition: uint256.cpp:64
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:34
256-bit opaque blob.
Definition: uint256.h:124
BOOST_AUTO_TEST_SUITE_END()
static bool is_null_key(const std::vector< unsigned char > &key)
BOOST_AUTO_TEST_CASE(dbwrapper)
const std::vector< unsigned char > & GetObfuscateKey(const CDBWrapper &w)
Work around circular dependency, as well as for testing in dbwrapper_tests.
Definition: dbwrapper.cpp:254
static bool exists(const path &p)
Definition: fs.h:77
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static uint256 InsecureRand256()
Definition: setup_common.h:66
static uint64_t InsecureRandBits(int bits)
Definition: setup_common.h:67
static uint32_t InsecureRand32()
Definition: setup_common.h:65
static bool InsecureRandBool()
Definition: setup_common.h:69
Basic testing setup.
Definition: setup_common.h:76
void Serialize(Stream &s) const
StringContentsSerializer(const std::string &inp)
StringContentsSerializer & operator+=(const StringContentsSerializer &s)
StringContentsSerializer & operator+=(const std::string &s)
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164