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