diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 3896ea64da5..4eb31c27526 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -186,6 +187,60 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) } } +// Verify that Read() returns false (without throwing) when the stored value +// fails to be deserialized +BOOST_AUTO_TEST_CASE(dbwrapper_read_returns_false_on_deserialization_error) +{ + for (const bool obfuscate : {false, true}) { + const fs::path path{m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_deser_obf" : "dbwrapper_deser_noobf")}; + CDBWrapper dbw({.path = path, .cache_bytes = 1 << 20, .wipe_data = true, .obfuscate = obfuscate}); + + constexpr uint8_t key{'X'}; + + // Write a single byte. uint256 requires 32 bytes, so reading this key + // as uint256 must trigger a deserialization error inside Read() + dbw.Write(key, uint8_t{0xFF}); + BOOST_CHECK(dbw.Exists(key)); + + // Read() must catch the deserialization exception and return false, + // the same as if the key were absent + uint256 result; + BOOST_CHECK(!dbw.Read(key, result)); + } +} + +// Verify Read() throws dbwrapper_error due to an internal db error +BOOST_AUTO_TEST_CASE(dbwrapper_read_throws_on_db_error) +{ + const fs::path path{m_args.GetDataDirBase() / "dbwrapper_db_error"}; + constexpr uint8_t key{'Y'}; + + const auto make_db = [] (const fs::path& path, const bool force_compact) { + return CDBWrapper({.path = path, .cache_bytes = 1 << 20, .obfuscate = false, + .options = {.force_compact = force_compact}}); + }; + + // Write a value and close the database + make_db(path, /*force_compact=*/false).Write(key, m_rng.rand256()); + + // Force compaction to ensure the data is written into the .ldb files + // rather than left in the WAL. + (void)make_db(path, /*force_compact=*/true); + + // Corrupt every table so any subsequent Read() fails + for (const auto& entry : fs::directory_iterator(path)) { + if (entry.path().extension() == ".ldb") { + std::ofstream{entry.path(), std::ios::binary | std::ios::trunc} + .write("\xff", 1); + } + } + + // Read() should detect the issue now and throw + const auto db{make_db(path, /*force_compact=*/false)}; + uint256 result; + BOOST_CHECK_EXCEPTION(db.Read(key, result), dbwrapper_error, HasReason("Fatal LevelDB error")); +} + BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { // Perform tests both obfuscated and non-obfuscated.