// Copyright (c) 2016-2020 The Hush developers // Copyright (c) 2018 The Zcash developers // Copyright (c) 2012-2017 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html #include "dbwrapper.h" #include "uint256.h" #include "random.h" #include "test/test_bitcoin.h" #include // for 'operator+=()' #include #include using namespace std; using namespace boost::assign; // bring 'operator+=()' into scope using namespace boost::filesystem; // Test if a string consists entirely of null characters bool is_null_key(const vector& key) { bool isnull = true; for (unsigned int i = 0; i < key.size(); i++) isnull &= (key[i] == '\x00'); return isnull; } BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(dbwrapper) { { path ph = temp_directory_path() / unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false); char key = 'k'; uint256 in = GetRandHash(); uint256 res; BOOST_CHECK(dbw.Write(key, in)); BOOST_CHECK(dbw.Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); } } // Test batch operations BOOST_AUTO_TEST_CASE(dbwrapper_batch) { { path ph = temp_directory_path() / unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false); char key = 'i'; uint256 in = GetRandHash(); char key2 = 'j'; uint256 in2 = GetRandHash(); char key3 = 'k'; uint256 in3 = GetRandHash(); uint256 res; CDBBatch batch(dbw); batch.Write(key, in); batch.Write(key2, in2); batch.Write(key3, in3); // Remove key3 before it's even been written batch.Erase(key3); dbw.WriteBatch(batch); BOOST_CHECK(dbw.Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); BOOST_CHECK(dbw.Read(key2, res)); BOOST_CHECK_EQUAL(res.ToString(), in2.ToString()); // key3 should've never been written BOOST_CHECK(dbw.Read(key3, res) == false); } } BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { { path ph = temp_directory_path() / unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false); // The two keys are intentionally chosen for ordering char key = 'j'; uint256 in = GetRandHash(); BOOST_CHECK(dbw.Write(key, in)); char key2 = 'k'; uint256 in2 = GetRandHash(); BOOST_CHECK(dbw.Write(key2, in2)); boost::scoped_ptr it(const_cast(&dbw)->NewIterator()); // Be sure to seek past any earlier key (if it exists) it->Seek(key); char key_res; uint256 val_res; it->GetKey(key_res); it->GetValue(val_res); BOOST_CHECK_EQUAL(key_res, key); BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString()); it->Next(); it->GetKey(key_res); it->GetValue(val_res); BOOST_CHECK_EQUAL(key_res, key2); BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString()); it->Next(); BOOST_CHECK_EQUAL(it->Valid(), false); } } BOOST_AUTO_TEST_CASE(iterator_ordering) { path ph = temp_directory_path() / unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false); for (int x=0x00; x<256; ++x) { uint8_t key = x; uint32_t value = x*x; BOOST_CHECK(dbw.Write(key, value)); } boost::scoped_ptr it(const_cast(&dbw)->NewIterator()); for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); for (int x=seek_start; x<256; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure break; BOOST_CHECK(it->GetKey(key)); BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key, x); BOOST_CHECK_EQUAL(value, x*x); it->Next(); } BOOST_CHECK(!it->Valid()); } } struct StringContentsSerializer { // Used to make two serialized objects the same while letting them have different lengths // This is a terrible idea string str; StringContentsSerializer() {} StringContentsSerializer(const string& inp) : str(inp) {} StringContentsSerializer& operator+=(const string& s) { str += s; return *this; } StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { if (ser_action.ForRead()) { str.clear(); char c = 0; while (true) { try { READWRITE(c); str.push_back(c); } catch (const std::ios_base::failure& e) { break; } } } else { for (size_t i = 0; i < str.size(); i++) READWRITE(str[i]); } } }; BOOST_AUTO_TEST_CASE(iterator_string_ordering) { char buf[10]; path ph = temp_directory_path() / unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false); for (int x=0x00; x<10; ++x) { for (int y = 0; y < 10; y++) { int n = snprintf(buf, sizeof(buf), "%d", x); assert(n > 0 && n < sizeof(buf)); StringContentsSerializer key(buf); for (int z = 0; z < y; z++) key += key; uint32_t value = x*x; BOOST_CHECK(dbw.Write(key, value)); } } boost::scoped_ptr it(const_cast(&dbw)->NewIterator()); for (int seek_start : {0, 5}) { int n = snprintf(buf, sizeof(buf), "%d", seek_start); assert(n > 0 && n < sizeof(buf)); StringContentsSerializer seek_key(buf); it->Seek(seek_key); for (int x=seek_start; x<10; ++x) { for (int y = 0; y < 10; y++) { int n = snprintf(buf, sizeof(buf), "%d", x); assert(n > 0 && n < sizeof(buf)); string exp_key(buf); for (int z = 0; z < y; z++) exp_key += exp_key; StringContentsSerializer key; uint32_t value; BOOST_CHECK(it->Valid()); if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure break; BOOST_CHECK(it->GetKey(key)); BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key.str, exp_key); BOOST_CHECK_EQUAL(value, x*x); it->Next(); } } BOOST_CHECK(!it->Valid()); } } BOOST_AUTO_TEST_SUITE_END()