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