Bitcoin Core  27.99.0
P2P Digital Currency
db_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-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 #if defined(HAVE_CONFIG_H)
7 #endif
8 
9 #include <boost/test/unit_test.hpp>
10 
11 #include <test/util/setup_common.h>
12 #include <util/check.h>
13 #include <util/fs.h>
14 #include <util/translation.h>
15 #ifdef USE_BDB
16 #include <wallet/bdb.h>
17 #endif
18 #ifdef USE_SQLITE
19 #include <wallet/sqlite.h>
20 #endif
21 #include <wallet/test/util.h>
22 #include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
23 
24 #include <fstream>
25 #include <memory>
26 #include <string>
27 
28 inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
29 {
30  Span key{kv.first}, value{kv.second};
31  os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
32  << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\")";
33  return os;
34 }
35 
36 namespace wallet {
37 
38 static Span<const std::byte> StringBytes(std::string_view str)
39 {
40  return AsBytes<const char>({str.data(), str.size()});
41 }
42 
43 static SerializeData StringData(std::string_view str)
44 {
45  auto bytes = StringBytes(str);
46  return SerializeData{bytes.begin(), bytes.end()};
47 }
48 
50 {
51  std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
52  MockableData actual;
53  while (true) {
54  DataStream key, value;
55  DatabaseCursor::Status status = cursor->Next(key, value);
56  if (status == DatabaseCursor::Status::DONE) break;
59  actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
60  }
61  BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
62 }
63 
64 BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
65 
66 static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
67 {
68  fs::path data_file = BDBDataFile(path);
69  database_filename = data_file.filename();
70  return GetBerkeleyEnv(data_file.parent_path(), false);
71 }
72 
73 BOOST_AUTO_TEST_CASE(getwalletenv_file)
74 {
75  fs::path test_name = "test_name.dat";
76  const fs::path datadir = m_args.GetDataDirNet();
77  fs::path file_path = datadir / test_name;
78  std::ofstream f{file_path};
79  f.close();
80 
81  fs::path filename;
82  std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
83  BOOST_CHECK_EQUAL(filename, test_name);
84  BOOST_CHECK_EQUAL(env->Directory(), datadir);
85 }
86 
87 BOOST_AUTO_TEST_CASE(getwalletenv_directory)
88 {
89  fs::path expected_name = "wallet.dat";
90  const fs::path datadir = m_args.GetDataDirNet();
91 
92  fs::path filename;
93  std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
94  BOOST_CHECK_EQUAL(filename, expected_name);
95  BOOST_CHECK_EQUAL(env->Directory(), datadir);
96 }
97 
98 BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
99 {
100  fs::path datadir = m_args.GetDataDirNet() / "1";
101  fs::path datadir_2 = m_args.GetDataDirNet() / "2";
102  fs::path filename;
103 
104  std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
105  std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
106  std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
107 
108  BOOST_CHECK(env_1 == env_2);
109  BOOST_CHECK(env_2 != env_3);
110 }
111 
112 BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
113 {
114  fs::path datadir = gArgs.GetDataDirNet() / "1";
115  fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
116  fs::path filename;
117 
118  std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
119  std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
120  env_1_a.reset();
121 
122  std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
123  std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
124 
125  BOOST_CHECK(env_1_a != env_1_b);
126  BOOST_CHECK(env_2_a == env_2_b);
127 }
128 
129 static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
130 {
131  std::vector<std::unique_ptr<WalletDatabase>> dbs;
132  DatabaseOptions options;
133  DatabaseStatus status;
134  bilingual_str error;
135 #ifdef USE_BDB
136  dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
137 #endif
138 #ifdef USE_SQLITE
139  dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
140 #endif
141  dbs.emplace_back(CreateMockableWalletDatabase());
142  return dbs;
143 }
144 
145 BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
146 {
147  // Test each supported db
148  for (const auto& database : TestDatabases(m_path_root)) {
149  std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
150 
151  // Write elements to it
152  std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
153  for (unsigned int i = 0; i < 10; i++) {
154  for (const auto& prefix : prefixes) {
155  BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
156  }
157  }
158 
159  // Now read all the items by prefix and verify that each element gets parsed correctly
160  for (const auto& prefix : prefixes) {
161  DataStream s_prefix;
162  s_prefix << prefix;
163  std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
164  DataStream key;
165  DataStream value;
166  for (int i = 0; i < 10; i++) {
167  DatabaseCursor::Status status = cursor->Next(key, value);
169 
170  std::string key_back;
171  unsigned int i_back;
172  key >> key_back >> i_back;
173  BOOST_CHECK_EQUAL(key_back, prefix);
174 
175  unsigned int value_back;
176  value >> value_back;
177  BOOST_CHECK_EQUAL(value_back, i_back);
178  }
179 
180  // Let's now read it once more, it should return DONE
181  BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
182  }
183  }
184 }
185 
186 // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
187 // covered in the higher level test above. The higher level test uses
188 // serialized strings which are prefixed with string length, so it doesn't test
189 // truly empty prefixes or prefixes that begin with \xff
190 BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
191 {
192  const MockableData::value_type
193  e{StringData(""), StringData("e")},
194  p{StringData("prefix"), StringData("p")},
195  ps{StringData("prefixsuffix"), StringData("ps")},
196  f{StringData("\xff"), StringData("f")},
197  fs{StringData("\xffsuffix"), StringData("fs")},
198  ff{StringData("\xff\xff"), StringData("ff")},
199  ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
200  for (const auto& database : TestDatabases(m_path_root)) {
201  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
202  for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
203  batch->Write(Span{k}, Span{v});
204  }
205  CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
206  CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
207  CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
208  CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
209  }
210 }
211 
212 BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
213 {
214  // Ensures the database remains accessible without deadlocking after a write error.
215  // To simulate the behavior, record overwrites are disallowed, and the test verifies
216  // that the database remains active after failing to store an existing record.
217  for (const auto& database : TestDatabases(m_path_root)) {
218  // Write original record
219  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
220  std::string key = "key";
221  std::string value = "value";
222  std::string value2 = "value_2";
223  BOOST_CHECK(batch->Write(key, value));
224  // Attempt to overwrite the record (expect failure)
225  BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
226  // Successfully overwrite the record
227  BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
228  // Sanity-check; read and verify the overwritten value
229  std::string read_value;
230  BOOST_CHECK(batch->Read(key, read_value));
231  BOOST_CHECK_EQUAL(read_value, value2);
232  }
233 }
234 
235 // Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
236 // Keys are in the form of std::pair<TYPE, ENTRY_ID>
237 BOOST_AUTO_TEST_CASE(erase_prefix)
238 {
239  const std::string key = "key";
240  const std::string key2 = "key2";
241  const std::string value = "value";
242  const std::string value2 = "value_2";
243  auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
244 
245  for (const auto& database : TestDatabases(m_path_root)) {
246  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
247 
248  // Write two entries with the same key type prefix, a third one with a different prefix
249  // and a fourth one with the type-id values inverted
250  BOOST_CHECK(batch->Write(make_key(key, value), value));
251  BOOST_CHECK(batch->Write(make_key(key, value2), value2));
252  BOOST_CHECK(batch->Write(make_key(key2, value), value));
253  BOOST_CHECK(batch->Write(make_key(value, key), value));
254 
255  // Erase the ones with the same prefix and verify result
256  BOOST_CHECK(batch->TxnBegin());
257  BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
258  BOOST_CHECK(batch->TxnCommit());
259 
260  BOOST_CHECK(!batch->Exists(make_key(key, value)));
261  BOOST_CHECK(!batch->Exists(make_key(key, value2)));
262  // Also verify that entries with a different prefix were not erased
263  BOOST_CHECK(batch->Exists(make_key(key2, value)));
264  BOOST_CHECK(batch->Exists(make_key(value, key)));
265  }
266 }
267 
268 #ifdef USE_SQLITE
269 
270 // Test-only statement execution error
271 constexpr int TEST_SQLITE_ERROR = -999;
272 
273 class DbExecBlocker : public SQliteExecHandler
274 {
275 private:
276  SQliteExecHandler m_base_exec;
277  std::set<std::string> m_blocked_statements;
278 public:
279  DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
280  int Exec(SQLiteDatabase& database, const std::string& statement) override {
281  if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
282  return m_base_exec.Exec(database, statement);
283  }
284 };
285 
286 BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
287 {
288  // Verifies that there is no active dangling, to-be-reversed db txn
289  // after the batch object that initiated it is destroyed.
290  DatabaseOptions options;
291  DatabaseStatus status;
292  bilingual_str error;
293  std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
294 
295  std::string key = "key";
296  std::string value = "value";
297 
298  std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
299  BOOST_CHECK(batch->TxnBegin());
300  BOOST_CHECK(batch->Write(key, value));
301  // Set a handler to prevent txn abortion during destruction.
302  // Mimicking a db statement execution failure.
303  batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
304  // Destroy batch
305  batch.reset();
306 
307  // Ensure there is no dangling, to-be-reversed db txn
308  BOOST_CHECK(!database->HasActiveTxn());
309 
310  // And, just as a sanity check; verify that new batchs only write what they suppose to write
311  // and nothing else.
312  std::string key2 = "key2";
313  std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
314  BOOST_CHECK(batch2->Write(key2, value));
315  // The first key must not exist
316  BOOST_CHECK(!batch2->Exists(key));
317 }
318 
319 BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
320 {
321  std::string key = "key";
322  std::string value = "value";
323  std::string value2 = "value_2";
324 
325  DatabaseOptions options;
326  DatabaseStatus status;
327  bilingual_str error;
328  const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
329 
330  std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
331 
332  // Verify concurrent db transactions does not interfere between each other.
333  // Start db txn, write key and check the key does exist within the db txn.
334  BOOST_CHECK(handler->TxnBegin());
335  BOOST_CHECK(handler->Write(key, value));
336  BOOST_CHECK(handler->Exists(key));
337 
338  // But, the same key, does not exist in another handler
339  std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
340  BOOST_CHECK(handler2->Exists(key));
341 
342  // Attempt to commit the handler txn calling the handler2 methods.
343  // Which, must not be possible.
344  BOOST_CHECK(!handler2->TxnCommit());
345  BOOST_CHECK(!handler2->TxnAbort());
346 
347  // Only the first handler can commit the changes.
348  BOOST_CHECK(handler->TxnCommit());
349  // And, once commit is completed, handler2 can read the record
350  std::string read_value;
351  BOOST_CHECK(handler2->Read(key, read_value));
352  BOOST_CHECK_EQUAL(read_value, value);
353 
354  // Also, once txn is committed, single write statements are re-enabled.
355  // Which means that handler2 can read the record changes directly.
356  BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
357  BOOST_CHECK(handler2->Read(key, read_value));
358  BOOST_CHECK_EQUAL(read_value, value2);
359 }
360 #endif // USE_SQLITE
361 
363 } // namespace wallet
ArgsManager gArgs
Definition: args.cpp:41
#define Assert(val)
Identity function.
Definition: check.h:77
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:232
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
const_iterator begin() const
Definition: streams.h:177
const_iterator end() const
Definition: streams.h:179
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
CONSTEXPR_IF_NOT_DEBUG Span< C > first(std::size_t count) const noexcept
Definition: span.h:205
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
path filename() const
Definition: fs.h:72
RAII class that provides access to a WalletDatabase.
Definition: db.h:45
virtual std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix)=0
BOOST_AUTO_TEST_SUITE_END()
std::ostream & operator<<(std::ostream &os, const std::pair< const SerializeData, SerializeData > &kv)
Definition: db_tests.cpp:28
Filesystem operations and types.
std::unique_ptr< BerkeleyDatabase > MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley database at specified path.
Definition: bdb.cpp:934
static void CheckPrefix(DatabaseBatch &batch, Span< const std::byte > prefix, MockableData expected)
Definition: db_tests.cpp:49
static std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &path, fs::path &database_filename)
Definition: db_tests.cpp:66
static SerializeData StringData(std::string_view str)
Definition: db_tests.cpp:43
std::unique_ptr< SQLiteDatabase > MakeSQLiteDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: sqlite.cpp:696
static std::vector< std::unique_ptr< WalletDatabase > > TestDatabases(const fs::path &path_root)
Definition: db_tests.cpp:129
fs::path BDBDataFile(const fs::path &wallet_path)
Definition: db.cpp:64
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition: util.cpp:190
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory, bool use_shared_memory)
Get BerkeleyEnvironment given a directory path.
Definition: bdb.cpp:79
BOOST_AUTO_TEST_CASE(bnb_search_test)
static Span< const std::byte > StringBytes(std::string_view str)
Definition: db_tests.cpp:38
std::map< SerializeData, SerializeData, std::less<> > MockableData
Definition: util.h:55
DatabaseStatus
Definition: db.h:196
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
const char * prefix
Definition: rest.cpp:1007
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:1008
Basic testing setup.
Definition: setup_common.h:52
Bilingual messages:
Definition: translation.h:18
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.
Definition: zeroafterfree.h:49