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