2022-12-24 23:49:50 +00:00
|
|
|
// Copyright (c) 2018-2022 The Bitcoin Core developers
|
2018-09-14 21:10:20 -04:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2024-10-10 12:20:34 +02:00
|
|
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
2024-02-13 08:03:02 +01:00
|
|
|
|
2018-09-14 21:10:20 -04:00
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
|
2019-11-05 15:18:59 -05:00
|
|
|
#include <test/util/setup_common.h>
|
2023-06-14 15:07:36 +01:00
|
|
|
#include <util/check.h>
|
2023-03-15 11:18:06 +01:00
|
|
|
#include <util/fs.h>
|
2022-04-11 17:11:37 -04:00
|
|
|
#include <util/translation.h>
|
|
|
|
#ifdef USE_BDB
|
2020-06-15 14:29:29 -04:00
|
|
|
#include <wallet/bdb.h>
|
2022-04-11 17:11:37 -04:00
|
|
|
#endif
|
|
|
|
#ifdef USE_SQLITE
|
|
|
|
#include <wallet/sqlite.h>
|
|
|
|
#endif
|
2024-01-02 16:36:23 -05:00
|
|
|
#include <wallet/migrate.h>
|
2022-04-11 17:11:37 -04:00
|
|
|
#include <wallet/test/util.h>
|
|
|
|
#include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
|
2018-09-14 21:10:20 -04:00
|
|
|
|
2020-06-11 08:58:46 +02:00
|
|
|
#include <fstream>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2018-09-14 21:10:20 -04:00
|
|
|
|
2022-04-11 17:11:37 -04:00
|
|
|
inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
|
|
|
|
{
|
|
|
|
Span key{kv.first}, value{kv.second};
|
|
|
|
os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
|
2024-10-05 23:56:47 +02:00
|
|
|
<< std::string_view{reinterpret_cast<const char*>(value.data()), value.size()} << "\")";
|
2022-04-11 17:11:37 -04:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2021-11-12 11:13:29 -05:00
|
|
|
namespace wallet {
|
2022-04-11 17:11:37 -04:00
|
|
|
|
2024-12-18 15:36:27 +01:00
|
|
|
inline std::span<const std::byte> StringBytes(std::string_view str)
|
2022-04-11 17:11:37 -04:00
|
|
|
{
|
2024-12-18 15:36:27 +01:00
|
|
|
return std::as_bytes(std::span{str});
|
2022-04-11 17:11:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static SerializeData StringData(std::string_view str)
|
|
|
|
{
|
|
|
|
auto bytes = StringBytes(str);
|
|
|
|
return SerializeData{bytes.begin(), bytes.end()};
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CheckPrefix(DatabaseBatch& batch, Span<const std::byte> prefix, MockableData expected)
|
|
|
|
{
|
|
|
|
std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
|
|
|
|
MockableData actual;
|
|
|
|
while (true) {
|
|
|
|
DataStream key, value;
|
|
|
|
DatabaseCursor::Status status = cursor->Next(key, value);
|
|
|
|
if (status == DatabaseCursor::Status::DONE) break;
|
|
|
|
BOOST_CHECK(status == DatabaseCursor::Status::MORE);
|
|
|
|
BOOST_CHECK(
|
|
|
|
actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
|
|
|
|
}
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
|
|
|
|
}
|
|
|
|
|
2018-09-14 21:10:20 -04:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
|
|
|
|
|
2022-03-03 14:40:18 -05:00
|
|
|
static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
|
2020-10-30 16:41:23 -04:00
|
|
|
{
|
|
|
|
fs::path data_file = BDBDataFile(path);
|
2022-03-03 14:40:18 -05:00
|
|
|
database_filename = data_file.filename();
|
2021-12-02 21:21:05 +01:00
|
|
|
return GetBerkeleyEnv(data_file.parent_path(), false);
|
2020-10-30 16:41:23 -04:00
|
|
|
}
|
|
|
|
|
2018-09-14 21:10:20 -04:00
|
|
|
BOOST_AUTO_TEST_CASE(getwalletenv_file)
|
|
|
|
{
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path test_name = "test_name.dat";
|
2021-12-02 21:21:05 +01:00
|
|
|
const fs::path datadir = m_args.GetDataDirNet();
|
2018-09-14 21:10:20 -04:00
|
|
|
fs::path file_path = datadir / test_name;
|
2020-06-11 08:58:46 +02:00
|
|
|
std::ofstream f{file_path};
|
2018-09-14 21:10:20 -04:00
|
|
|
f.close();
|
|
|
|
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path filename;
|
2018-09-14 21:10:20 -04:00
|
|
|
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
|
2021-02-09 15:00:02 +01:00
|
|
|
BOOST_CHECK_EQUAL(filename, test_name);
|
|
|
|
BOOST_CHECK_EQUAL(env->Directory(), datadir);
|
2018-09-14 21:10:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
|
|
|
|
{
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path expected_name = "wallet.dat";
|
2021-12-02 21:21:05 +01:00
|
|
|
const fs::path datadir = m_args.GetDataDirNet();
|
2018-09-14 21:10:20 -04:00
|
|
|
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path filename;
|
2018-09-14 21:10:20 -04:00
|
|
|
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
|
2021-02-09 15:00:02 +01:00
|
|
|
BOOST_CHECK_EQUAL(filename, expected_name);
|
|
|
|
BOOST_CHECK_EQUAL(env->Directory(), datadir);
|
2018-09-14 21:10:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
|
|
|
|
{
|
2021-12-02 21:21:05 +01:00
|
|
|
fs::path datadir = m_args.GetDataDirNet() / "1";
|
|
|
|
fs::path datadir_2 = m_args.GetDataDirNet() / "2";
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path filename;
|
2018-09-14 21:10:20 -04:00
|
|
|
|
|
|
|
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
|
|
|
|
std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
|
|
|
|
std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
|
|
|
|
|
|
|
|
BOOST_CHECK(env_1 == env_2);
|
|
|
|
BOOST_CHECK(env_2 != env_3);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
|
|
|
|
{
|
2021-05-04 13:00:25 +02:00
|
|
|
fs::path datadir = gArgs.GetDataDirNet() / "1";
|
|
|
|
fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
|
2022-03-03 14:40:18 -05:00
|
|
|
fs::path filename;
|
2018-09-14 21:10:20 -04:00
|
|
|
|
|
|
|
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
|
|
|
|
std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
|
|
|
|
env_1_a.reset();
|
|
|
|
|
|
|
|
std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
|
|
|
|
std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
|
|
|
|
|
|
|
|
BOOST_CHECK(env_1_a != env_1_b);
|
|
|
|
BOOST_CHECK(env_2_a == env_2_b);
|
|
|
|
}
|
|
|
|
|
2022-04-11 17:11:37 -04:00
|
|
|
static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
|
|
|
|
{
|
|
|
|
std::vector<std::unique_ptr<WalletDatabase>> dbs;
|
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
|
|
|
bilingual_str error;
|
|
|
|
#ifdef USE_BDB
|
|
|
|
dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
|
2024-01-02 16:36:23 -05:00
|
|
|
// Needs BDB to make the DB to read
|
|
|
|
dbs.emplace_back(std::make_unique<BerkeleyRODatabase>(BDBDataFile(path_root / "bdb"), /*open=*/false));
|
2022-04-11 17:11:37 -04:00
|
|
|
#endif
|
|
|
|
#ifdef USE_SQLITE
|
|
|
|
dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
|
|
|
|
#endif
|
|
|
|
dbs.emplace_back(CreateMockableWalletDatabase());
|
|
|
|
return dbs;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
|
|
|
|
{
|
|
|
|
// Test each supported db
|
|
|
|
for (const auto& database : TestDatabases(m_path_root)) {
|
|
|
|
std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
|
|
|
|
|
2023-06-14 15:07:36 +01:00
|
|
|
std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
|
2024-01-02 16:36:23 -05:00
|
|
|
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
|
|
|
// For BerkeleyRO, open the file now. This must happen after BDB has written to the file
|
|
|
|
database->Open();
|
|
|
|
} else {
|
|
|
|
// Write elements to it if not berkeleyro
|
|
|
|
for (unsigned int i = 0; i < 10; i++) {
|
|
|
|
for (const auto& prefix : prefixes) {
|
|
|
|
BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
|
|
|
|
}
|
2022-04-11 17:11:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now read all the items by prefix and verify that each element gets parsed correctly
|
|
|
|
for (const auto& prefix : prefixes) {
|
|
|
|
DataStream s_prefix;
|
|
|
|
s_prefix << prefix;
|
|
|
|
std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
|
|
|
|
DataStream key;
|
|
|
|
DataStream value;
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
DatabaseCursor::Status status = cursor->Next(key, value);
|
2023-06-14 15:07:36 +01:00
|
|
|
BOOST_CHECK_EQUAL(status, DatabaseCursor::Status::MORE);
|
2022-04-11 17:11:37 -04:00
|
|
|
|
|
|
|
std::string key_back;
|
|
|
|
unsigned int i_back;
|
|
|
|
key >> key_back >> i_back;
|
|
|
|
BOOST_CHECK_EQUAL(key_back, prefix);
|
|
|
|
|
|
|
|
unsigned int value_back;
|
|
|
|
value >> value_back;
|
|
|
|
BOOST_CHECK_EQUAL(value_back, i_back);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let's now read it once more, it should return DONE
|
|
|
|
BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
|
|
|
|
}
|
2024-01-02 16:36:23 -05:00
|
|
|
handler.reset();
|
|
|
|
database->Close();
|
2022-04-11 17:11:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
|
|
|
|
// covered in the higher level test above. The higher level test uses
|
|
|
|
// serialized strings which are prefixed with string length, so it doesn't test
|
|
|
|
// truly empty prefixes or prefixes that begin with \xff
|
|
|
|
BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
|
|
|
|
{
|
|
|
|
const MockableData::value_type
|
|
|
|
e{StringData(""), StringData("e")},
|
|
|
|
p{StringData("prefix"), StringData("p")},
|
|
|
|
ps{StringData("prefixsuffix"), StringData("ps")},
|
|
|
|
f{StringData("\xff"), StringData("f")},
|
|
|
|
fs{StringData("\xffsuffix"), StringData("fs")},
|
|
|
|
ff{StringData("\xff\xff"), StringData("ff")},
|
|
|
|
ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
|
|
|
|
for (const auto& database : TestDatabases(m_path_root)) {
|
|
|
|
std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
|
2024-01-02 16:36:23 -05:00
|
|
|
|
|
|
|
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
|
|
|
// For BerkeleyRO, open the file now. This must happen after BDB has written to the file
|
|
|
|
database->Open();
|
|
|
|
} else {
|
|
|
|
// Write elements to it if not berkeleyro
|
|
|
|
for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
|
|
|
|
batch->Write(Span{k}, Span{v});
|
|
|
|
}
|
2022-04-11 17:11:37 -04:00
|
|
|
}
|
2024-01-02 16:36:23 -05:00
|
|
|
|
2022-04-11 17:11:37 -04:00
|
|
|
CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
|
|
|
|
CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
|
|
|
|
CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
|
|
|
|
CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
|
2024-01-02 16:36:23 -05:00
|
|
|
batch.reset();
|
|
|
|
database->Close();
|
2022-04-11 17:11:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-20 12:07:13 -03:00
|
|
|
BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
|
|
|
|
{
|
|
|
|
// Ensures the database remains accessible without deadlocking after a write error.
|
|
|
|
// To simulate the behavior, record overwrites are disallowed, and the test verifies
|
|
|
|
// that the database remains active after failing to store an existing record.
|
|
|
|
for (const auto& database : TestDatabases(m_path_root)) {
|
2024-01-02 16:36:23 -05:00
|
|
|
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
|
|
|
// Skip this test if BerkeleyRO
|
|
|
|
continue;
|
|
|
|
}
|
2023-12-20 12:07:13 -03:00
|
|
|
// Write original record
|
|
|
|
std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
|
|
|
|
std::string key = "key";
|
|
|
|
std::string value = "value";
|
|
|
|
std::string value2 = "value_2";
|
|
|
|
BOOST_CHECK(batch->Write(key, value));
|
|
|
|
// Attempt to overwrite the record (expect failure)
|
|
|
|
BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
|
|
|
|
// Successfully overwrite the record
|
|
|
|
BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
|
|
|
|
// Sanity-check; read and verify the overwritten value
|
|
|
|
std::string read_value;
|
|
|
|
BOOST_CHECK(batch->Read(key, read_value));
|
|
|
|
BOOST_CHECK_EQUAL(read_value, value2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:28:23 -03:00
|
|
|
// Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
|
|
|
|
// Keys are in the form of std::pair<TYPE, ENTRY_ID>
|
|
|
|
BOOST_AUTO_TEST_CASE(erase_prefix)
|
|
|
|
{
|
|
|
|
const std::string key = "key";
|
|
|
|
const std::string key2 = "key2";
|
|
|
|
const std::string value = "value";
|
|
|
|
const std::string value2 = "value_2";
|
|
|
|
auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
|
|
|
|
|
|
|
|
for (const auto& database : TestDatabases(m_path_root)) {
|
2024-01-02 16:36:23 -05:00
|
|
|
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
|
|
|
// Skip this test if BerkeleyRO
|
|
|
|
continue;
|
|
|
|
}
|
2024-02-06 16:28:23 -03:00
|
|
|
std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
|
|
|
|
|
|
|
|
// Write two entries with the same key type prefix, a third one with a different prefix
|
|
|
|
// and a fourth one with the type-id values inverted
|
|
|
|
BOOST_CHECK(batch->Write(make_key(key, value), value));
|
|
|
|
BOOST_CHECK(batch->Write(make_key(key, value2), value2));
|
|
|
|
BOOST_CHECK(batch->Write(make_key(key2, value), value));
|
|
|
|
BOOST_CHECK(batch->Write(make_key(value, key), value));
|
|
|
|
|
|
|
|
// Erase the ones with the same prefix and verify result
|
|
|
|
BOOST_CHECK(batch->TxnBegin());
|
|
|
|
BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
|
|
|
|
BOOST_CHECK(batch->TxnCommit());
|
|
|
|
|
|
|
|
BOOST_CHECK(!batch->Exists(make_key(key, value)));
|
|
|
|
BOOST_CHECK(!batch->Exists(make_key(key, value2)));
|
|
|
|
// Also verify that entries with a different prefix were not erased
|
|
|
|
BOOST_CHECK(batch->Exists(make_key(key2, value)));
|
|
|
|
BOOST_CHECK(batch->Exists(make_key(value, key)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:19:25 -03:00
|
|
|
#ifdef USE_SQLITE
|
|
|
|
|
|
|
|
// Test-only statement execution error
|
|
|
|
constexpr int TEST_SQLITE_ERROR = -999;
|
|
|
|
|
|
|
|
class DbExecBlocker : public SQliteExecHandler
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
SQliteExecHandler m_base_exec;
|
|
|
|
std::set<std::string> m_blocked_statements;
|
|
|
|
public:
|
|
|
|
DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
|
|
|
|
int Exec(SQLiteDatabase& database, const std::string& statement) override {
|
|
|
|
if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
|
|
|
|
return m_base_exec.Exec(database, statement);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
|
|
|
|
{
|
|
|
|
// Verifies that there is no active dangling, to-be-reversed db txn
|
|
|
|
// after the batch object that initiated it is destroyed.
|
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
|
|
|
bilingual_str error;
|
|
|
|
std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
|
|
|
|
|
|
|
|
std::string key = "key";
|
|
|
|
std::string value = "value";
|
|
|
|
|
|
|
|
std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
|
|
|
|
BOOST_CHECK(batch->TxnBegin());
|
|
|
|
BOOST_CHECK(batch->Write(key, value));
|
|
|
|
// Set a handler to prevent txn abortion during destruction.
|
|
|
|
// Mimicking a db statement execution failure.
|
|
|
|
batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
|
|
|
|
// Destroy batch
|
|
|
|
batch.reset();
|
|
|
|
|
|
|
|
// Ensure there is no dangling, to-be-reversed db txn
|
|
|
|
BOOST_CHECK(!database->HasActiveTxn());
|
|
|
|
|
|
|
|
// And, just as a sanity check; verify that new batchs only write what they suppose to write
|
|
|
|
// and nothing else.
|
|
|
|
std::string key2 = "key2";
|
|
|
|
std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
|
|
|
|
BOOST_CHECK(batch2->Write(key2, value));
|
|
|
|
// The first key must not exist
|
|
|
|
BOOST_CHECK(!batch2->Exists(key));
|
|
|
|
}
|
|
|
|
|
2023-12-23 12:49:41 -03:00
|
|
|
BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
|
|
|
|
{
|
|
|
|
std::string key = "key";
|
|
|
|
std::string value = "value";
|
|
|
|
std::string value2 = "value_2";
|
2024-01-08 11:19:25 -03:00
|
|
|
|
2023-12-23 12:49:41 -03:00
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
|
|
|
bilingual_str error;
|
|
|
|
const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
|
|
|
|
|
|
|
|
std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
|
|
|
|
|
|
|
|
// Verify concurrent db transactions does not interfere between each other.
|
|
|
|
// Start db txn, write key and check the key does exist within the db txn.
|
|
|
|
BOOST_CHECK(handler->TxnBegin());
|
|
|
|
BOOST_CHECK(handler->Write(key, value));
|
|
|
|
BOOST_CHECK(handler->Exists(key));
|
|
|
|
|
|
|
|
// But, the same key, does not exist in another handler
|
|
|
|
std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
|
|
|
|
BOOST_CHECK(handler2->Exists(key));
|
|
|
|
|
|
|
|
// Attempt to commit the handler txn calling the handler2 methods.
|
|
|
|
// Which, must not be possible.
|
|
|
|
BOOST_CHECK(!handler2->TxnCommit());
|
|
|
|
BOOST_CHECK(!handler2->TxnAbort());
|
|
|
|
|
|
|
|
// Only the first handler can commit the changes.
|
|
|
|
BOOST_CHECK(handler->TxnCommit());
|
|
|
|
// And, once commit is completed, handler2 can read the record
|
|
|
|
std::string read_value;
|
|
|
|
BOOST_CHECK(handler2->Read(key, read_value));
|
|
|
|
BOOST_CHECK_EQUAL(read_value, value);
|
|
|
|
|
|
|
|
// Also, once txn is committed, single write statements are re-enabled.
|
|
|
|
// Which means that handler2 can read the record changes directly.
|
|
|
|
BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
|
|
|
|
BOOST_CHECK(handler2->Read(key, read_value));
|
|
|
|
BOOST_CHECK_EQUAL(read_value, value2);
|
|
|
|
}
|
|
|
|
#endif // USE_SQLITE
|
2023-12-20 12:07:13 -03:00
|
|
|
|
2018-09-14 21:10:20 -04:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|
2021-11-12 11:13:29 -05:00
|
|
|
} // namespace wallet
|