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