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